5

来自 mdn:传播语法

注意:通常 ES2015 中的扩展运算符在复制数组时会深一层。因此,它们不适合复制多维数组。这与 Object.assign() 和 Object spread 语法的情况相同。请查看下面的示例以获得更好的理解。

var a = [[1], [2], [3]];
var b = [...a];
b.shift().shift(); // 1
// Now array b is: [[2], [3]]

上述声明的意义何在?上面的代码示例与使用 .slice() 方法将 a 中的数组复制到 b 中的工作方式相同。我尝试在此处向数组添加另一个维度:https ://repl.it/HKOq/2并且事情仍然按预期工作。

那么为什么扩展语法不适合复制多维数组呢?

我会很感激任何帮助。

编辑:

阅读 estus 和 vol7ron 的答案帮助我解决了问题。基本上,正如 estus 在技术上指出的那样,数组中只有数组,而不是多维数组。

正如 vol7ron 解释的那样,仅复制数组的第一级,因此内存中的对象对于任何进一步的嵌套元素都保持不变。

我也错误地怀疑使用扩展语法的行为应该与切片运算符有任何不同

4

4 回答 4

13

伙计,程序员真的不擅长展示实际显示差异的示例。

var a = [[['a', 'b'], ['c', 'd']], 'e'];
var b = [...a];
b[0][0][0] = 'z';
b[1] = 'x';
console.log('a', a);
console.log('b', b);

这输出:

a [[["z", "b"], ["c", "d"]], "e"]
b [[["z", "b"], ["c", "d"]], "x"]

注意到一些可疑的东西吗?两个数组[0][0][0]的值都已更改。这意味着[0][0][0]位于两个数组中的对象都引用了同一个对象,而不是一个副本。但是[1]值不同意味着它确实是一个副本

浅拷贝是指拷贝第一层,引用更深的层。

于 2017-04-21T09:41:36.680 回答
5

数组是对象,并[...a]创建数组对象的拷贝a

对于语言本身,没有多维数组 - 数组中还有另一个数组。是否包含数组、普通对象、函数或原语都没有关系。对于基元,它们的值将被复制。否则,将复制对对象的引用。这是什么

Object.assign() 和 Object 扩展运算符的情况相同

部分指。

而关于

上面的代码示例与使用 .slice() 方法将 a 中的数组复制到 b 的工作方式相同

……确实如此。这是一种更简洁的书写方式a.slice()or [].concat(a)。有相当大的例外。ES6 rest 运算符(以及Array.from(a))对所有可迭代对象同样有效,而不仅仅是数组。

对于一个对象的拷贝,ES6 没有提供任何新东西,一个对象(数组就是)应该手动递归地拷贝。为了解决所有问题,使用经过验证的第三方辅助函数(例如 Lodash)仍然是有意义的cloneDeep

于 2017-04-15T03:26:01.600 回答
1

不会为内部数组元素创建新数组(对于多维数组):

// One-dimensional array
var a = [1,2,3];
var b = [...a];

a[0]='a';
console.log('a',a);
console.log('b',b);   
  // expected: b[0] == 1
  // got:      b[0] == 1



// Multi-dimensional array
var a = [[1], [2], [3]];
var b = [...a];

a[0][0]='a';
console.log('a',a);
console.log('b',b);   
  // expected: b[0][0] == 1
  // got:      b[0][0] == 'a'

它的工作原理类似于slice(),因此您必须遍历数组并为每个维度创建新数组。这是一个简单的例子:

// Multi-dimensional array
var a = [[1], [2], [3]];
var b = (function fn(ar){
 return ar.map(el=>Array.isArray(el)&&fn(el)||el) 
})(a);

a[0][0]='a';
console.log('a',a);
console.log('b',b);   
  // expected: b[0][0] == 1
  // got:      b[0][0] == 1

于 2017-04-15T03:20:57.563 回答
0

因此,该示例试图传达的是不会var b = [...a];展开(a eg )的内部数组b = [1,2,3],而是展开。所以删除并返回第一个元素是,然后第二个只是从返回的数组中删除。一言以蔽之,仅在您的扩展数组中向下达到一级,例如相当于,而不是在示例中b[[1],[2],[3]]b.shift()b[1]shift()1...var b =[...a]var b = [a[0], a[1], a[2]]var b = [ a[0][0], a[1][0], a[2][0] ]

于 2017-04-15T03:04:32.590 回答