es6之三个点运算符

…(此处应有乌鸦飞过)运算符

当我和别人聊天的时候,别人说了一句让我很无语的话的时候,我会输入…,或者double…,es6新特性中,三个点竟成了运算符,我们来看看到底es6委员会辛辛苦苦到底做了什么,会不会让我们让我们无语的输入…

言归正传,...作为运算符时,学名叫扩展(spread)运算符,允许一个可迭代对象在位置上按序展开。比如一个数组字面量,使用了扩展运算符就可以将元素迭代展开,作为函数参数被调用的数组字面量也可以使用该运算符。如果作用在一个对象(该对象必须可迭代)字面量上,此时的作用就是迭代键值对(对象迭代处于ECMAScript Stage 3的草案中)。

可迭代对象和迭代器部分,可以参考这篇文章,异步编程之generator

函数调用

代替apply

1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用方法
var spreadExample = (x,y,z) => { }
var args = [1,2,3];
// 一般的做法:spread_example_1.apply(null, args)
// 通过扩展运算符将数组展开成函数的三个参数的实参
spreadExample(...args)
// 扩展运算符与正常的函数参数可以结合使用
var f = (v, w, x, y, z) => { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);
//

new构造函数

当用new调用构造函数时,不能直接用数组和apply,但是有了扩展运算符,数组就可以轻松调用了

1
2
var dateFields = [1970, 0, 1]; // 1 Jan 1970
var d = new Date(...dateFields);

数组字面量

扩展运算符后的变量必须为可迭代对象,在可迭代对象内部通过for…of遍历值,所以可以方便的进行一些操作

合并数组

1
2
3
4
5
var arr1 = [1,2,3];
var arr2 = [4,5,6];
arr2.unshift(...arr1);// or arr1.push(...arr2);
//==========================//
var arr3 = [...arr1, ...arr2];

复制数组

1
2
3
4
5
6
7
8
var arr = [1,2,3];
var arr2 = [...arr];
arr2.push(4); // [1,2,3,4]
//===========================//
var arr3 = [{a:1},{b:1},{c:1}];
var arr4 = [...arr3];
arr4[0].a = 2 // arr3: [{a:2},{b:1},{c:1}]
// 等同于arr.slice(),Object.assign(),并且均为浅拷贝,对于引用类型需要注意

生成数组

根据扩展运算符可以跟数组的原理,可以通过解构赋值,将数组反求出来

1
2
3
4
5
var [first, ...rest] = [1,2,3,4];
// first 1
// rest [2,3,4]
// 注意:解构反求数组的参数只能放到最后一位,否则会报错
var [...head, tail] = [1,2,3,4] // Uncaught SyntaxError: Rest element must be last element

当然扩展运算符不止可以用于数组,只要是可迭代对象都可以,所以另一种用途就是将其他可迭代对象转化为数组,比如SetMapGenerator

1
2
3
4
var f = () => {
// 替代Array.prototype.slice.apply(arguments)
let args = [...arguments];
}

可迭代对象(stage 3 draft)

ECMAScript proposal (stage 3)为对象增加了==扩展属性==,复制自身可枚举属性到一个新的对象中。

浅拷贝或者对象合并现在可以丢掉Object.assign()使用一种更简洁的写法了。不同之处是,Object.assign()可以触发setters,扩展属性不可以

1
2
3
4
5
6
7
8
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }

与之对应的有一个叫rest运算符,是spread的逆运算,两者可以对比着学习

函数rest参数

...+变量,在函数定义的时候称为rest参数,这样就不用再使用arguments对象了,rest参数将参数放入数组中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var rest_example = (...values) => {
// 由于定义了rest参数,所以函数内部接受到的values为一个数组
count = 0;
for(let i of values) {
count++;
console.log('第'+count+'次:',i);
}
}
// 传入1,2,3
rest_example(1,2,3)
// 第1次: 1
// 第2次: 2
// 第3次: 3
// 如果我传入的是[1,2,3]呢
rest_example([1,2,3])
// 第1次: (3) [1, 2, 3]
// 可以看出此时只遍历了一次
// 总结:不管我传的是什么,使用了rest参数后,在内部都会包装成一个数组

注意:

  • 和spread运算符类似的是,都必须放到最后一位,否则报错
  • 函数的length属性不包括rest参数