在 ES6 中新增了兩種運運算元,一種是擴充套件運運算元,另一種是 rest 運運算元。這兩種運運算元可以很好地解決函數引數和陣列元素長度未知情況下的編碼問題,使得程式碼能更加健壯和簡潔。
接下來會通過範例具體講解擴充套件運運算元的使用場景。
擴充套件運運算元
擴充套件運運算元用 3個點表示(...),用於將一個陣列或類陣列物件轉換為用逗號分隔的值序列。
它的基本用法是拆解陣列和字串。
const array = [1, 2, 3, 4];
console.log(...array); // 1 2 3 4
const str = "string";
console.log(...str); // s t r i n g
在上面的程式碼中陣列型別變數 array 和字串型別變數 str 在經過擴充套件運運算元的處理後,得到的都是單獨的值序列。
基於擴充套件運運算元拆解陣列的特性,它有很多應用場景,接下來將一一講解。
擴充套件運運算元代替 apply() 函數
擴充套件運運算元可以代替 apply() 函數,將陣列轉換為函數引數。
例如,獲取陣列最大值時,使用 apply() 函數的寫法如下所示:
let arr = [1, 4, 6, 8, 2];
console.log(Math.max.apply(null, arr)); // 8
如果使用擴充套件運運算元,可以如下面所示的程式碼這樣寫,實現簡化程式碼。
console.log(Math.max(...arr)); // 8
例如,自定義一個 add() 函數,用於接收兩個引數,並返回兩個引數相加的和。當傳遞的引數是一個陣列時,如果使用 apply() 函數,寫法如下:
function add (num1, num2) {
return num1 + num2;
}
const arr = [1, 3];
add.apply(null, arr); // 4
如果使用擴充套件運運算元,寫法如下:
add(...arr); // 4
擴充套件運運算元代替 concat() 函數合併陣列
在 ES5 中,合併陣列時,我們會使用 concat() 函數,寫法如下:
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
console.log(arr1.concat(arr2)); // [ 1, 2, 3, 4, 5, 6 ]
如果使用擴充套件運運算元,寫法如下:
console.log([...arr1, ...arr2]); // [ 1, 2, 3, 4, 5, 6 ]
擴充套件運運算元轉換 Set,得到去重的陣列
Set 具有自動的去重性質,我們可以再次使用擴充套件運運算元將 Set 結構轉換成陣列。
let arr = [1, 2, 4, 6, 2, 7, 4];
console.log([...new Set(arr)]); // [ 1, 2, 4, 6, 7 ]
擴充套件運運算元用於物件克隆
使用擴充套件運運算元實現物件克隆,這種方式存在一定的侷限性,具體描述如下:
首先我們來看使用擴充套件運運算元克隆的物件的屬性值為基本資料型別的場景。
let obj = {name: 'kingx'};
var obj2 = {...obj};
obj2.name = 'kingx2';
console.log(obj); // {name: "kingx"}
在上面的範例中,obj 物件包含一個 name 屬性,它的值為一個字串,使用擴充套件運運算元對 obj 物件進行克隆得到 obj2 物件,在改變 obj2 物件的 name 屬性值為 kingx2 後,輸出 obj 物件的值,此時發現 name 屬性值仍然為 kingx。
這表明對克隆後物件的值進行修改並未影響到被克隆的物件,那麼是不是就意味著使用擴充套件運運算元實現的是深克隆呢?
並不是的,我們再來看看下面這個例子,這是克隆物件的屬性值為參照資料型別的場景。
let obj3 = {
name: 'kingx',
address: {province: 'guangdong', city: 'guangzhou'}
};
let obj4 = {...obj3};
obj4.name = 'kingx3';
obj4.address.city = 'shenzhen';
console.log(obj3);
// {name: "kingx", address: {province: "guangdong", city: "shenzhen"}}
在上面的範例中,obj3 物件包含 name 和 address 兩個屬性,其中 address 屬性值為參照資料型別。
在使用擴充套件運運算元副本後得到 obj4 物件,對 obj4 物件的 name 屬性和 address.city 屬性進行修改然後輸出 obj3 物件,發現 name 屬性值並未修改,而 address.city值變為了 shenzhen。
表明對克隆後物件的值進行更改後,影響到了被克隆的物件,這就意味著使用擴充套件運運算元的克隆並不是嚴格的深克隆。
上面的場景同樣適用於陣列,當陣列的元素為基本資料型別時,可以實現深克隆,而陣列中出現參照資料型別元素的時候,就不再是深克隆。
let arr1 = [1, 3, 4, 6]; // 可以進行深克隆
let arr2 = [1, 3, [4, 6]]; // 不可以進行深克隆
總結上面的描述,得到的結論是:
-
使用擴充套件運運算元對陣列或物件進行克隆時,如果陣列的元素或者物件的屬性是基本資料型別,則支援深克隆;
-
如果是參照資料型別,則不支援深克隆。歸根結底是因為參照資料型別的克隆只是複製了參照的地址,克隆後的物件仍然共用同一個參照地址。