ES6+ 展開(kāi)語(yǔ)法
1. 前言
ES6 新增了 ...
的語(yǔ)法糖,主要用于展開(kāi)語(yǔ)法和剩余語(yǔ)法中,本節(jié)先來(lái)說(shuō)說(shuō)展開(kāi)語(yǔ)法。展開(kāi)語(yǔ)法顧名思義可以理解為把整體展開(kāi)成個(gè)體,在 ES5 中如果想把一個(gè)數(shù)組的內(nèi)容拷貝到另一個(gè)數(shù)組中,可以使用 for
循環(huán)數(shù)組的每一項(xiàng),然后添加到目標(biāo)數(shù)組中去。但是如果使用展開(kāi)語(yǔ)法就很方便地完成這個(gè)操作了。下面我們就來(lái)看看展開(kāi)語(yǔ)法是如何使用的。
2. 從拷貝說(shuō)起
2.1 數(shù)組拷貝
在 ES5 經(jīng)常會(huì)遇到數(shù)組和對(duì)象的淺拷貝,我們都知道數(shù)組和對(duì)象都是引用類型,所以不能像字符串那樣直接賦值,在 ES5 中數(shù)組和對(duì)象的拷貝都是通過(guò)循環(huán)來(lái)實(shí)現(xiàn)的,下面我們來(lái)看幾個(gè)例子:
var arr1 = [1, 2, 3];
var arr2 = [];
arr1.forEach(function(value){
arr2.push(value);
})
console.log(arr2); // [1, 2, 3]
上面的代碼是把 arr1 數(shù)組中的項(xiàng)拷貝到 arr2 中去,還可以使用數(shù)組提供的 concat 和 slice 方法來(lái)實(shí)現(xiàn)。
var arr1 = [1, 2, 3];
var arr2 = [].concat(arr1);
var arr3 = arr1.slice(0);
arr1.push(4)
console.log(arr1); //[1, 2, 3, 4]
console.log(arr2); //[1, 2, 3]
console.log(arr3); //[1, 2, 3]
在拷貝完后,對(duì) arr1 數(shù)組添加元素,可以看到 arr2 和 arr3 沒(méi)有發(fā)生變化,說(shuō)明它們不是一個(gè)引用地址。這是 ES5 所提供的拷貝方式,那么 ES6 是如何簡(jiǎn)化的呢?
var arr1 = [1, 2, 3];
var arr2 = [...arr1];
arr1.push(4)
console.log(arr1); //[1, 2, 3, 4]
console.log(arr2); //[1, 2, 3]
使用 ...
展開(kāi)語(yǔ)法可以實(shí)現(xiàn)與上面 ES5 實(shí)現(xiàn)的相同效果,而且比較簡(jiǎn)潔地表達(dá)了把 arr1 中的每一項(xiàng)展開(kāi)放入 arr2 中。
2.2 字面量對(duì)象拷貝
上面說(shuō)到了 ES5 和 ES6 數(shù)組拷貝的一個(gè)對(duì)比,那么針對(duì)字面量對(duì)象的拷貝二者又是怎么來(lái)實(shí)現(xiàn)的呢?
ES5 中針對(duì)字面量對(duì)象的拷貝方式比較少,沒(méi)有數(shù)組提供的類似的方法可以使用,只能使用循環(huán),但是還可以使用 JSON.stringify
和 JSON.parse
來(lái)實(shí)現(xiàn),但是這個(gè)方法存在一些缺陷。 下面看 ES5 中字面量的拷貝實(shí)例:
let obj = {a: 1, b: 2};
let copy1 = {};
for(let key in obj) {
copy1[key] = obj[key]
}
let copy2 = JSON.parse(JSON.stringify(obj))
上面的兩種方法給出了 ES5 拷貝字面量對(duì)象的方法,比較麻煩,也容易出錯(cuò)。ES6 給出了它的答案:
let obj = {a: 1, b: 2};
let copy = {...obj};
使用展開(kāi)語(yǔ)法對(duì) obj 進(jìn)行展開(kāi),完美地實(shí)現(xiàn)了拷貝過(guò)程。
Tips: 這里有必要說(shuō)明一下,以上的方法都是淺拷貝(只拷貝數(shù)組和對(duì)象的第一層結(jié)構(gòu))的過(guò)程,對(duì)于數(shù)組和對(duì)象第一層以后的內(nèi)容,如果是引用類型的存儲(chǔ)方式,則不會(huì)進(jìn)行拷貝操作,也就是不會(huì)進(jìn)行深拷貝。
3. 語(yǔ)法詳情
上面通過(guò)拷貝初步了解了展開(kāi)語(yǔ)法,這里我們給出展開(kāi)語(yǔ)法的定義:展開(kāi)語(yǔ)法在函數(shù)調(diào)用和構(gòu)造數(shù)組時(shí),將字符串和數(shù)組在語(yǔ)法層面展開(kāi);如果是對(duì)象時(shí),將對(duì)象的表達(dá)式按照 key-value
的方式展開(kāi)。展開(kāi)語(yǔ)法的使用主要有以下幾種:
- 處理字符串、數(shù)組和字面量對(duì)象;
- 簡(jiǎn)化函數(shù)調(diào)用時(shí)傳參問(wèn)題;
- 代替 apply 方法。
3.1 在字符串中的使用
展開(kāi)語(yǔ)法在處理字符串時(shí),顧名思義可以把字符進(jìn)行展開(kāi),從而得到一個(gè)每項(xiàng)都是單個(gè)字符串的數(shù)組,注意展開(kāi)語(yǔ)法在字符串使用時(shí),需要包裹在 []
中才能生效。
const arr = [...'imooc'];
console.log(arr); // ["i", "m", "o", "o", "c"]
在 ES5 中也有方法,可以使用 split
方法實(shí)現(xiàn)把字符串變成數(shù)組。
const arr = 'imooc'.split('');
console.log(arr); // ["i", "m", "o", "o", "c"]
3.2 在數(shù)組中的使用
上面我們講了 ES5 中對(duì)一個(gè)數(shù)組的拷貝,在數(shù)組的操作中還有添加、合并等操作的時(shí)候,需要調(diào)用數(shù)組的 slice ()
、concat ()
、unshift ()
等方法,或者組合使用這些方法。
const arr1 = [1, 2];
const arr2 = ['a', ...arr1];
const arr3 = [...arr1, ...arr2];
console.log(arr2); // ['a', 1, 2]
console.log(arr3); // [1, 2, 'a', 1, 2]
上面的代碼可以看出,展開(kāi)語(yǔ)法有很多種不同的使用方式,我們可以把展開(kāi)語(yǔ)法當(dāng)成一個(gè)整體,直接放到想放到的位置上即可,擴(kuò)展了操作數(shù)組的方式。
3.3 在字面量對(duì)象中的使用
和數(shù)組一樣,展開(kāi)語(yǔ)法在字面量對(duì)象中的使用方式也有很多種:
const obj1 = {a: 1, b: 2};
const obj2 = {...obj1, c: 30};
console.log(obj2); // {a:1, b:2, c:30}
const obj3 = {b: 20, c: 30};
const obj4 = {...obj2, ...obj3}; // 合并對(duì)象
console.log(obj4); // {a:1, b:20, c:30}
上面的代碼可以看出,使用方式和數(shù)組基本一致,都是把數(shù)組或?qū)ο笾械拿恳豁?xiàng)展開(kāi)到另一個(gè)數(shù)組或?qū)ο笾腥ァ?/p>
3.4 函數(shù)調(diào)用時(shí)傳參問(wèn)題
在函數(shù)調(diào)用時(shí)經(jīng)常會(huì)向函數(shù)中傳遞參數(shù),但是,當(dāng)我們的參數(shù)是數(shù)組中的項(xiàng)時(shí),我們需要把數(shù)組中的每一項(xiàng)取出來(lái),然后傳入函數(shù)中,這樣顯得很麻煩,能不能有個(gè)方式直接把數(shù)組傳入進(jìn)去呢?首先我們看個(gè)求和的例子:
function sum(x, y) {
return x + y;
}
console.log(sum(1, 2)); // 3
const data = [1,2];
console.log(sum(data[0], data[1])); // 3
上面的 sum 是一個(gè)求和函數(shù),接受兩個(gè)參數(shù),我們可以在調(diào)用時(shí)直接傳遞 2 個(gè)參數(shù)。但這個(gè)時(shí)候我們希望求 data 數(shù)組中的和,這個(gè)時(shí)候只能取出 data 中的每一項(xiàng)值傳遞到函數(shù)中去,這樣無(wú)疑是一個(gè)很笨的方法,在 ES5 的時(shí)候可以使用 apply()
對(duì)函數(shù)進(jìn)行間接的調(diào)用解決這個(gè)問(wèn)題。
function sum(x, y, z) {
return x + y + z;
}
const data = [1,2,3];
console.log(sum.apply(null, data)); // 6
使用 apply()
的方法是解決了這個(gè)問(wèn)題,但是可能會(huì)使我們理解代碼增加了難度。有了 ES6 的展開(kāi)語(yǔ)法,這個(gè)問(wèn)題就會(huì)輕而易舉地解決了。
function sum(x, y, z) {
return x + y + z;
}
const data = [1,2,3];
console.log(sum(...data)); // 6
上面的方法使用展開(kāi)語(yǔ)法把 data 數(shù)組中的每一項(xiàng)進(jìn)行展開(kāi),成為 sum 函數(shù)中的三個(gè)參數(shù)。
4. 小結(jié)
本節(jié)通過(guò)數(shù)組和字面量的拷貝引入了 ES6 的展開(kāi)語(yǔ)法的優(yōu)勢(shì),又說(shuō)到了在函數(shù)傳參時(shí)的應(yīng)用,可以總結(jié)以下幾點(diǎn):
- 可以把
...
加數(shù)組或字面量當(dāng)作數(shù)組或字面量中的一項(xiàng),任意放入數(shù)組或字面量中不同的位置; - 可以通過(guò)展開(kāi)語(yǔ)法對(duì)數(shù)組和字面量進(jìn)行淺拷貝;
- 在函數(shù)傳參數(shù)直接把數(shù)組中的項(xiàng)進(jìn)行展開(kāi)就可以達(dá)到傳遞多個(gè)參數(shù)的目的。