扩展语句

 

 扩展语法允许一个表达式在期望多个参数(用于函数调用)或多个元素(用于数组字面量)或多个变量(用于解构赋值)的位置扩展。

语法

用于函数调用:

myFunction(...iterableObj);

用于数组字面量:

const [...iterableObj] = [1, 3, 5, 7, 9];
[...iterableObj, 0, 2, 4, 6, 8];
// [1, 3, 5, 7, 9, 0, 2, 4, 6, 8]
[0, 2, ...iterableObj, 4, 6, 8];
// [0, 2, 1, 3, 5, 7, 9, 4, 6, 8]
[...iterableObj, 0, 2, 4, 5, 6, 8, ...iterableObj];
// [1, 3, 5, 7, 9, 0, 2, 4, 5, 6, 8, 1, 3, 5, 7, 9]

用于对象字面量 (new in ECMAScript; stage 3 draft):

let objClone = { ...obj};

 ...rest 必须是参数列表的最后一个参数!

...spread 无限制!

 

范例

更好的 apply 方法

在需要使用数组作为函数的参数的情况下,通常使用 Function.prototype.apply 方法:

function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);

如果使用了ES6的展开运算符,你可以这么写:

function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);

还可以同时展开多个数组:

function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);

一个更强大的数组字面量

例子:  如果已经有一个数组,此时还需要再新建一个数组,要求新数组包含已有数组的数组项的话,就要用到pushspliceconcat 等数组方法。有了扩展运算符会让代码更简洁:

let parts = ['shoulder', 'knees'];
let Tshirts = ['Lee', 'Nike'];
let lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulder", "knees", "and", "toes"]
let lyrics = ['head', ...parts, 'and', 'toes', ...Tshirts];
// ["head", "shoulder", "knees", "and", "toes", "Lee", "Nike"]

就像扩展参数列表一样,...可以在数组字面量中的任何地方使用,可以多次使用。

配合new运算符

例子: 在ES5中,我们无法同时使用 new 运算符和 apply 方法(apply方法调用[[Call]]而不是[[Construct]])。在ES6中,我们可以使用扩展运算符,和普通的函数调用一样。

let dateFields = [1970, 0, 1];  
// 1 Jan 1970
let d = new Date(...dateFields);
let dateFields = readDateFields(database);
let d = new Date(...dateFields);

复制一个数组


let arr = [1, 2, 3];
let arr2 = [...arr]; // 就像是 arr.slice()
arr2.push(4);
console.log(arr2) // [1, 2, 3, 4]
// arr 不受影响

Note: 复制数组时候, 拓展语句只会进行浅复制, 因此如下所示, 它并不适合复制多维数组 (与Object.assign() 相同)。


let a = [[1], [2], [3]];
let b = [...a];
b.shift().shift(); // 1
console.log(b)// [[2], [3]]

一个更好的连接数组的方法

例子: 在ES5中,我们通常使用 pushconcat 方法将一个或多个元素添加到另一个数组的末尾:


var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// 使用push将arr2中的所有元素添加到arr1中
Array.prototype.push.apply(arr1, arr2);
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// 使用concat链接两个数组
arr1.concat(arr2);

在ES6中,使用展开运算符(spread syntax):

let [arr1, arr2] = [[0, 1, 2], [3, 4, 5]]; // 解构赋值
arr1.push(...arr2);
let [arr1, arr2] = [[0, 1, 2], [3, 4, 5]];
//直接连接
console.log([...arr1, ...arr2]); // [0, 1, 2, 3, 4, 5]

例子: 使用 unshift 将一个或多个元素添加到数组的开头


var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// 将arr2所有元素添加至arr1开头
Array.prototype.unshift.apply(arr1, arr2);
console.log(arr1) // [3, 4, 5, 0, 1, 2]

使用展开运算符(spread syntax):


let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1];
console.log(arr1) // [3, 4, 5, 0, 1, 2]

仅可遍历对象(iterables)可用

let obj = {'key1': 'value1'};
let array = [...obj];
// TypeError: obj is not iterable
// 必须是一个可迭代对象
let obj = {"key1":"value1"};
function myFunction(x) {
    console.log(x);
}
myFunction(...obj); // TypeError: obj is not iterable
let args = [...obj]; // TypeError: obj is not iterable
let iterableObj = {0: 'a', 1: 'b', length: 2, [Symbol.iterator]: Array.prototype[Symbol.iterator]}; 伪数组对象
console.log([...iterableObj]); //["a", "b"]

将类数组对象转换成数组

扩展运算符可以将一个类数组对象中索引范围在[0,length)之间的所有属性的值添加到一个数组中,这样就可以得到一个真正的数组:

var nodeList = document.querySelectorAll('div');
var array = [...nodeList];

剩余操作符

还有一种操作符叫做剩余操作符(the rest operator),它的样子看起来和展开操作符一样,但是它是用于解构数组和对象。在某种程度上,剩余元素和展开元素相反,展开元素会“展开”数组变成多个元素,剩余元素会收集多个元素和“压缩”成一个单一的元素。

规范

规范 状态 备注
ECMAScript 2015 (6th Edition, ECMA-262) Standard 规范中定义的几个部分: Array Initializer, Argument Lists
ECMAScript Latest Draft (ECMA-262) Living Standard  

 

浏览器兼容性

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
数组字面量中的展开操作 46 16 (16) Edge 未实现 7.1
函数调用中的展开操作 46 27 (27) Edge 未实现 7.1
解构赋值中的展开操作 49 34 (34) 未实现 ? ?
Feature Android Android Webview Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile Chrome for Android
数组字面量中的展开操作 未实现 46 16.0 (16) 未实现 未实现 8 46
函数调用中的展开操作 未实现 46 27.0 (27) 未实现 未实现 8 46
解构赋值中的展开操作 未实现 未实现 34 (34) ? ? ? 未实现

相关链接

文档标签和贡献者