基礎語法
陣列
// 基礎型別解構
let [a, b, c] = [1, 2, 3]
console.log(a, b, c) // 1, 2, 3
// 物件陣列解構
let [a, b, c] = [{name: '1'}, {name: '2'}, {name: '3'}]
console.log(a, b, c) // {name: '1'}, {name: '2'}, {name: '3'}
// ...解構
let [head, ...tail] = [1, 2, 3, 4]
console.log(head, tail) // 1, [2, 3, 4]
// 巢狀解構
let [a, [b], d] = [1, [2, 3], 4]
console.log(a, b, d) // 1, 2, 4
// 解構不成功為undefined
let [a, b, c] = [1]
console.log(a, b, c) // 1, undefined, undefined
// 解構預設賦值
let [a = 1, b = 2] = [3]
console.log(a, b) // 3, 2
複製程式碼
物件
// 物件屬性解構
let { f1, f2 } = { f1: 'test1', f2: 'test2' }
console.log(f1, f2) // test1, test2
// 可以不按照順序,這是陣列解構和物件解構的區別之一
let { f2, f1 } = { f1: 'test1', f2: 'test2' }
console.log(f1, f2) // test1, test2
// 解構物件重新命名
let { f1: rename, f2 } = { f1: 'test1', f2: 'test2' }
console.log(rename, f2) // test1, test2
// 巢狀解構
let { f1: {f11}} = { f1: { f11: 'test11', f12: 'test12' } }
console.log(f11) // test11
// 預設值
let { f1 = 'test1', f2: rename = 'test2' } = { f1: 'current1', f2: 'current2'}
console.log(f1, rename) // current1, current2
複製程式碼
函式引數
// 引數解構
function func1({ x, y }) {
return x + y
}
func1({ x: 1, y: 2}) // 3
function func1({ x = 1, y = 2 }) {
return x + y
}
func1({x: 4}) // 6
複製程式碼
String/Map/Set
// String
let [ a, b, c, ...rest ] = 'test123'
console.log(a, b, c, rest) // t, e, s, [ 't', '1', '2', '3' ]
// Map
let [a, b] = new Map().set('f1', 'test1').set('f2', 'test2')
console.log(a, b) // [ 'f1', 'test1' ], [ 'f2', 'test2' ]
// Set
let [a, b] = new Set([1, 2, 3])
console.log(a, b) // 1, 2
複製程式碼
解構原理
解構是ES6提供的語法糖,其實內在是針對可迭代物件
的Iterator介面
,通過遍歷器
按順序獲取對應的值進行賦值。這裡需要提前懂得ES6的兩個概念:
- Iterator
- 可迭代物件
Iterator概念
Iterator是一種介面,為各種不一樣的資料解構提供統一的訪問機制。任何資料解構只要有Iterator介面,就能通過遍歷操作,依次按順序處理資料結構內所有成員。ES6中的for of的語法相當於遍歷器,會在遍歷資料結構時,自動尋找Iterator介面。
Iterator作用:
- 為各種資料解構提供統一的訪問介面
- 使得資料解構能按次序排列處理
- 可以使用ES6最新命令 for of進行遍歷
function makeIterator(array) {
var nextIndex = 0
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++]} :
{done: true}
}
};
}
var it = makeIterator([0, 1, 2])
console.log(it.next().value) // 0
console.log(it.next().value) // 1
console.log(it.next().value) // 2
複製程式碼
可迭代物件
可迭代物件是Iterator介面的實現。這是ECMAScript 2015的補充,它不是內建或語法,而僅僅是協議
。任何遵循該協議點物件都能成為可迭代物件。可迭代物件得有兩個協議:可迭代協議和迭代器協議。
可迭代協議
:物件必須實現@@iterator方法。即物件或其原型鏈上必須有一個名叫Symbol.iterator
的屬性。該屬性的值為無參函式,函式返回迭代器協議。
屬性 | 值 |
---|---|
Symbol.iterator |
返回一個物件的無參函式,被返回物件符合迭代器協議。 |
迭代器協議
:定義了標準的方式來產生一個有限或無限序列值。其要求必須實現一個next()方法,該方法返回物件有done(boolean)和value屬性。
屬性 | 值 |
---|---|
next |
返回一個物件的無參函式,被返回物件擁有兩個屬性:done和valuedone - 如果迭代器已經經過了被迭代序列時為 true。這時 value 可能描述了該迭代器的返回值。如果迭代器可以產生序列中的下一個值,則為 false。這等效於連同 done 屬性也不指定。value - 迭代器返回的任何 JavaScript 值。done 為 true 時可省略。 |
通過以上可知,自定義資料結構,只要擁有Iterator介面,並將其部署到自己的Symbol.iterator屬性上,就可以成為可迭代物件,能被for of迴圈遍歷。
// 自定義可迭代物件
let obj = {
[Symbol.iterator] : function() {
return{
next: function() {
return { value: 1, done: true }
}
}
}
}
for (let item of obj) {
console.log(item) // 不會報錯,因為obj已經是可迭代物件
}
複製程式碼
解構語法糖
String、Array、Map、Set等原生資料結構都是可迭代物件,可以通過for of迴圈遍歷它。故可以通過ES6解構語法糖依次獲取對應的值。
// String
let str = 'test'
let iterFun = str[Symbol.iterator]
let iterator = str[Symbol.iterator]()
let first = iterator.next() // 等效於 let [first] = 'test'
console.log(iterFun, iterator, first)
// 列印
// [Function: [Symbol.iterator]], {}, { value: 't', done: false }
// Array
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
// 以下等效於 let [first, second, third, four] = ['a', 'b', 'c']
let first = iter.next() // { value: 'a', done: false }
let second = iter.next() // { value: 'b', done: false }
let third = iter.next() // { value: 'c', done: false }
let four = iter.next() // { value: undefined, done: true }
複製程式碼
原生object物件是預設沒有部署Iterator介面,即object不是一個可迭代物件。因為遍歷時,不知道到底哪個屬性先遍歷,哪個屬性後遍歷,需要開發者手動指定。不過object部署Iterator介面沒有必要,因為ES6提供了Map資料結構。實際上物件被解構時,會被當作Map進行解構。所以雖然Map和Object很多地方相似,但ES6引入Map、Set物件是有其原因的。