es6之擴充套件運算子 三個點(...)
es6之擴充套件運算子 三個點(…)
物件的擴充套件運算子
理解物件的擴充套件運算子其實很簡單,只要記住一句話就可以:
物件中的擴充套件運算子(...)用於取出引數物件中的所有可遍歷屬性,拷貝到當前物件之中
let bar = { a: 1, b: 2 };
let baz = { ...z }; // { a: 1, b: 2 }
上述方法實際上等價於:
let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }
Object.assign
方法用於物件的合併,將源物件(source)
的所有可列舉屬性,複製到目標物件(target)
Object.assign
方法的第一個引數是目標物件,後面的引數都是源物件。(如果目標物件與源物件有同名屬性,或多個源物件有同名屬性,則後面的屬性會覆蓋前面的屬性)。
同樣,如果使用者自定義的屬性,放在擴充套件運算子後面,則擴充套件運算子內部的同名屬性會被覆蓋掉。
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 3, b: 4}
利用上述特性就可以很方便的修改物件的部分屬性。在redux
中的reducer
函式規定必須是一個純函式(如果不是很清楚什麼是純函式的可以參考這裡),reducer
中的state
物件要求不能直接修改,可以通過擴充套件運算子把修改路徑的物件都複製一遍,然後產生一個新的物件返回。
這裡有點需要注意的是擴充套件運算子對物件例項的拷貝屬於一種淺拷貝。肯定有人要問什麼是淺拷貝?我們知道javascript
中有兩種資料型別,分別是基礎資料型別和引用資料型別。基礎資料型別是按值訪問的,常見的基礎資料型別有Number
、String
、Boolean
、Null
、Undefined
,這類變數的拷貝的時候會完整的複製一份;引用資料型別比如Array
,在拷貝的時候拷貝的是物件的引用,當原物件發生變化的時候,拷貝物件也跟著變化,比如:
let obj1 = { a: 1, b: 2};
let obj2 = { ...obj1, b: '2-edited'};
console.log(obj1) ; // {a: 1, b: 2}
console.log(obj2); // {a: 1, b: "2-edited"}
上面這個例子擴充套件運算子拷貝的物件是基礎資料型別,因此對obj2
的修改並不會影響obj1
,如果改成這樣:
let obj1 = { a: 1, b: 2, c: {nickName: 'd'}};
let obj2 = { ...obj1};
obj2 .c.nickName = 'd-edited';
console.log(obj1); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
console.log(obj2); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
這裡可以看到,對obj2
的修改影響到了被拷貝物件obj1
,原因上面已經說了,因為obj1
中的物件c
是一個引用資料型別,拷貝的時候拷貝的是物件的引用。
陣列的擴充套件運算子
擴充套件運算子同樣可以運用在對陣列的操作中。
- 可以將陣列轉換為引數序列
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
- 可以複製陣列
如果直接通過下列的方式進行陣列複製是不可取的:
const arr1 = [1, 2];
const arr2 = arr1;
arr2[0] = 2;
arr1 // [2, 2]
原因上面已經介紹過,用擴充套件運算子就很方便:
const arr1 = [1, 2];
const arr2 = [...arr1];
還是記住那句話:擴充套件運算子(…)用於取出引數物件中的所有可遍歷屬性,拷貝到當前物件之中,這裡引數物件是個陣列,數組裡面的所有物件都是基礎資料型別,將所有基礎資料型別重新拷貝到新的陣列中。
- 擴充套件運算子可以與解構賦值結合起來,用於生成陣列
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
需要注意的一點是:
如果將擴充套件運算子用於陣列賦值,只能放在引數的最後一位,否則會報錯。
const [...rest, last] = [1, 2, 3, 4, 5];
// 報錯
const [first, ...rest, last] = [1, 2, 3, 4, 5];
// 報錯
- 擴充套件運算子還可以將字串轉為真正的陣列
[...'hello']
// [ "h", "e", "l", "l", "o" ]
- 任何 Iterator 介面的物件(參閱 Iterator 一章),都可以用擴充套件運算子轉為真正的陣列
這點說的比較官方,大傢俱體可以參考阮一峰老師的ECMAScript 6入門教程。
比較常見的應用是可以將某些資料結構轉為陣列,比如:
// arguments物件
function foo() {
const args = [...arguments];
}
用於替換es5
中的Array.prototype.slice.call(arguments)
寫法。
總結
擴充套件運算子的用法就說到這裡,介紹的不全但都是些最常見的用法。