【系統學習ES6】
本專題旨在對ES6的常用技術點進行系統性梳理,幫助大家對其有更好的掌握,希望大家有所收穫。
ES6允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構。解構是一種打破資料結構,將其拆分為更小部分的過程。
為何使用解構
傳統寫法中,從陣列或物件中提取特定資料賦值給變數,編寫了很多結構相同,變數不同的程式碼,例如:
let option = { name:'foo', type:'string' }; let name = option.name; let type = option.type;
以上程式碼的作用是,從option物件中提取name和type兩個屬性,賦值給變數name和type。此時只有兩個變數,想象一下,如果有更多變數,那就要依次追加多句賦值。如果其中包含巢狀結構,那就要深入讀取層級,才能找到目標變數。
也許是為了解決這種困擾,所以ES6為物件和陣列新增了解構功能,在合適的場景下運用,大大減少程式碼量,提高工作效率。
解構成功的前提,是左右兩側型別一致,即:如果是物件解構,左側被賦值變數必須包裝在{ }內,同理,陣列解構,左側必須是[ ]。
物件解構
上文強調過:物件的解構,左側必須是{ }。只要左側的變數在右側物件中有,即使位置不同,也可被成功賦值,但如果變數在右側沒有與之對應的屬性,則會被賦值為undefined。
let option = { name:'foo', type:'string' }; let { name, type } = option; // let { type, name } = option; // 此句與上句效果相同 console.log(name); // 'foo' console.log(type); // 'string' let { age } = option; console.log(age); // undefined let { name } = option; console.log(name); // 'foo'
注意:物件解構前,必須提供初始值(即右側的物件)。如果右側沒有值,程式會拋異常。
let { name }; // Uncaught SyntaxError:
為非同名變數賦值
如果變數名與物件屬性名不一致,可以通過解構對變數賦值麼?ES6的另一個擴充語法可以幫助我們。
let option = { name:'foo', type:'string' }; let { name: attrName, type: attrType} = option; console.log(attrName); // 'foo' console.log(attrType); // 'string'
這實際上說明,物件的解構賦值是下面形式的簡寫:
let { name: attrName, type: attrType} = { name:'foo', type:'string' };
也就是說,物件的解構賦值的內部機制,是先找到同名屬性,然後再賦給對應的變數。真正被賦值的是後者,而不是前者。
巢狀賦值
可以單層解構賦值,就支援巢狀賦值。前提是左右兩邊形式一致。
let option = { name: 'foo', type: 'string', start: { row: 1, col: 0 } }; let { start: { row } } = option; console.log(row); // 1
陣列解構
陣列的解構與物件有一個重要的不同。物件的解構按屬性名決定的,左右兩側變數位置可以不一致,但變數必須與屬性同名,才能取到正確的值。
陣列的元素是按順序排列的,變數的取值由它的位置決定。只要等號兩邊型別相同(都是陣列格式),左側變數會按照位置關係,被賦予右側陣列的同的值。在這個過程中,右側陣列本身不會發生變化。
let [name, type] = ['foo','string']; console.log(name); // 'foo' console.log(type); // 'string'
巢狀賦值
let [a, [b, c] = [1, [2, 3]]; console.log(name); // 1 console.log(type); // 2 console.log(height); // 3
如果左右形式不一致,可能會直接報錯:
let [a] = 1; // Uncaught TypeError: 1 is not iterable
如果右側陣列元素數量比左側變數多,可以解構成功。屬於不完全匹配。
let [a, b] = [1, 2, 3]; console.log(a); // 1 console.log(b); // 2
如果右側陣列元素數量比左側變數少,這種情況屬於解構失敗,解構失敗的變數會被賦值為undefined。
let [a, b] = [1]; console.log(a); // 1 console.log(b); // undefined let [a, b, ...c] = [1]; console.log(a); // 1 console.log(b); // undefined console.log(b); // []
此時,b是undefined理解,那c為什麼是陣列?這不是解構的鍋,相關知識點,後面會討論到。
預設值設定
不管是陣列還是物件,解構賦值都允許指定預設值。只有當右側相應屬性或位置的值全等(===)undefined,預設值才會生效。
陣列:
let [a = 1] = []; // a=1 let [a, b = 2] = [1]; // a=1, b=2 let [a, b = 2] = [1, undefined]; // a=1, b=2 let [a, b = 2] = [1, null]; // a=1, b=null let [a, b = 2] = [1, ""]; // a=1, b=""
物件:
let { name, type = 'string' } = { name: 'foo'}; // name:'foo' type:'string' let { name, type = 'string' } = { name: 'foo', type: undefined}; // name:'foo' type:'string' let { name, type = 'string' } = { name: 'foo', type: null}; // name:'foo' type:'null' let { name, type = 'string' } = { name: 'foo', type: ''}; // name:'foo' type:''
圓括號的妙用
(1)如果要將一個已經宣告的變數用於解構賦值,要注意一下寫法。
let name = 'bar'; { name } = { name: 'foo' }; console.log(name); // SyntaxError: syntax error
上面程式碼會報錯,因為 JavaScript 引擎會將{name}理解成一個程式碼塊,從而發生語法錯誤。只有不將大括號寫在行首,才能解決這個問題。於是圓括號來了。陣列同理。
let name = 'bar'; ({ name } = { name: 'foo' }); console.log(name); // 'foo'
字串解構
字串也可以解構,這是因為,此時字串被轉換成了一個類似陣列的物件。
const [a,b,c,d,e]="lemoncool"; console.log(a); // l console.log(b); // e console.log(c); // m console.log(d); // 0 console.log(e); // n .....
混合解構
討論完物件和陣列的解構,其實可以基於二者來創造更復雜的表示式解構。比如json中,陣列和物件混雜,此時想解析到某個資訊。是否可以實現呢?答案是肯定的,前提是格式要對應正確,篇幅有限,此處不再舉例。
解構實用場景
解構,除了基礎的變數賦值外,用途還有很多。
1、交換變數的值
let x = 1; let y = 2; [x, y] = [y, x];
上面程式碼交換變數x和y的值,這樣的寫法不僅簡潔,而且易讀,語義非常清晰。
2、返回多個值
函式只能返回一個值,如果要返回多個值,只能將它們放在陣列或物件裡返回。有了解構賦值,取出這些值就非常方便。
function example() { return [1, 2, 3]; } let [a, b, c] = example();
3.引入腳手架中的指定方法
腳手架不想整體引入,可以指定引入某些方法。
import { cloneDeep, random } from 'lodash';
到此,變數解構的使用方法及注意事項已梳理完畢。
下一節我們會一起討論【擴充運算子和res運算子】,感謝大家支援,希望大家在每一節中都有所得。
公棕號【前端便利貼】記錄著一個程式媛的所見所得所想,分享日常技術筆記,內容覆蓋了閱讀、技術和個人思考~,關注公棕號更早獲取更多文章。
覺得有用的話,小手點點【推薦】再走吖~~