这个例子的输出是一样的,但是在底层的行为是不一样的,
考虑(检查浏览器的控制台):
var x = [], y = [];
x[1] = "a";
y[1] = "b";
var usingSpread = [...x, ...y];
var usingConcat = x.concat(y);
console.log(usingSpread); // [ undefined, "a", undefined, "b"]
console.log(usingConcat); // [ , "a", , "b"]
console.log(1 in usingSpread); // true
console.log(1 in usingConcat); // false
Array.prototype.concat将保留数组中的空槽undefined
,而 Spread 将用值替换它们。
输入Symbol.iterator和Symbol.isConcatSpreadable:
展开运算符使用该符号@@iterator
遍历数组和类似数组的对象,例如:
- Array.prototype
- TypedArray.prototype
- String.prototype
- Map.prototype
- 设置.prototype
(这就是为什么你可以使用for .. of
它们)
我们可以覆盖默认iterator
符号以查看spread
操作符的行为方式:
var myIterable = ["a", "b", "c"];
var myIterable2 = ["d", "e", "f"];
myIterable[Symbol.iterator] = function*() {
yield 1;
yield 2;
yield 3;
};
console.log(myIterable[0], myIterable[1], myIterable[2]); // a b c
console.log([...myIterable]); // [1,2,3]
var result = [...myIterable, ...myIterable2];
console.log(result); // [1,2,3,"d","e","f"]
var result2 = myIterable.concat(myIterable2);
console.log(result2); // ["a", "b", "c", "d", "e", "f"]
另一方面,@@isConcatSpreadable
是
一个布尔值属性,如果为 true,则表示对象应通过 Array.prototype.concat 展平为其数组元素。
如果设置为false
,Array.concat
则不会展平数组:
const alpha = ['a', 'b', 'c'];
const numeric = [1, 2, 3];
let alphaNumeric = alpha.concat(numeric);
// console.log(alphaNumeric);
numeric[Symbol.isConcatSpreadable] = false;
alphaNumeric = alpha.concat(numeric);
// alphaNumeric = [...alpha, ...numeric];
// the above line will output : ["a","b","c",1,2,3]
console.log(JSON.stringify(alphaNumeric)); // ["a","b","c",[1,2,3]]
但是,由于它们不可迭代,因此spread
行为不同Objects
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
var objCopy = {...obj}; // copy
它将自己的可枚举属性从提供的对象复制到新对象上。
扩展运算符更快,请检查spread-into-array-vs-concat(至少从 Chrome 67 开始)
并检查三个点如何在某些用例中更改 javascript,其中包括解构赋值(数组或对象):
const arr = [1, 2, 3, 4, 5, 6, 7];
const [first, , third, ...rest] = arr;
console.log({ first, third, rest });
并将字符串拆分为字符数组:
console.log( [...'hello'] ) // [ "h", "e" , "l" , "l", "o" ]