为什么展开元素不适用于复制多维数组?
Why is a spread element unsuitable for copying multidimensional arrays?
来自 mdn:Spread Syntax
Note: Typically the spread operators in ES2015 goes one level deep while copying an array. Therefore, they are unsuitable for copying multidimensional arrays. It's the same case with Object.assign() and Object spread syntax. Look at the example below for a better understanding.
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 所解释的那样,只有数组的第一层被复制,因此内存中的对象对于任何进一步的嵌套元素都保持不变。
我也错误地怀疑使用扩展语法的行为应该与切片运算符有任何不同
所以这个例子试图表达的是 var b = [...a];
不会 展开 a
的内部数组(例如 b = [1,2,3]
) ,但 b
将变为 [[1],[2],[3]]
。因此 b.shift()
删除并 returns b
的第一个元素 [1]
,然后第二个 shift()
只是从返回的数组中删除 1
。一言以蔽之,...
仅到达您的 spreaded 数组的一个级别,例如var b =[...a]
等同于 var b = [a[0], a[1], a[2]]
,而不是示例中的 var b = [ a[0][0], a[1][0], a[2][0] ]
没有为内部数组元素创建新数组(对于多维数组):
// 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
数组是对象,[...a]
创建浅数组对象的a
副本。
对于语言本身,没有多维数组 - 数组中还有另一个数组。是否包含数组、普通对象、函数或原语并不重要。对于基元,它们的值将被复制。否则,将复制对对象的引用。这就是
It's the same case with Object.assign() and Object spread operators
部分是指。
关于
The above code sample works just the same as if you'd copied the array in a to b using the .slice() method
...确实如此。这是 a.slice()
或 [].concat(a)
的更简洁的写法。除了一个相当大的例外。 ES6 剩余运算符(以及 Array.from(a)
)对所有可迭代对象同样有效,而不仅仅是数组。
对于对象的 深度 副本 ES6 没有提供任何新内容,对象(数组就是)应该手动递归复制。为了解决所有问题,使用经过验证的第三方辅助函数仍然有意义,例如 Lodash cloneDeep
.
伙计,程序员真的很不擅长显示实际显示差异的示例。
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]
的对象被 引用 到同一个对象,而不是 copy。然而 [1]
值不同意味着它确实是 copy。
浅拷贝表示第一层复制,深层次引用.
来自 mdn:Spread Syntax
Note: Typically the spread operators in ES2015 goes one level deep while copying an array. Therefore, they are unsuitable for copying multidimensional arrays. It's the same case with Object.assign() and Object spread syntax. Look at the example below for a better understanding.
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 所解释的那样,只有数组的第一层被复制,因此内存中的对象对于任何进一步的嵌套元素都保持不变。
我也错误地怀疑使用扩展语法的行为应该与切片运算符有任何不同
所以这个例子试图表达的是 var b = [...a];
不会 展开 a
的内部数组(例如 b = [1,2,3]
) ,但 b
将变为 [[1],[2],[3]]
。因此 b.shift()
删除并 returns b
的第一个元素 [1]
,然后第二个 shift()
只是从返回的数组中删除 1
。一言以蔽之,...
仅到达您的 spreaded 数组的一个级别,例如var b =[...a]
等同于 var b = [a[0], a[1], a[2]]
,而不是示例中的 var b = [ a[0][0], a[1][0], a[2][0] ]
没有为内部数组元素创建新数组(对于多维数组):
// 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
数组是对象,[...a]
创建浅数组对象的a
副本。
对于语言本身,没有多维数组 - 数组中还有另一个数组。是否包含数组、普通对象、函数或原语并不重要。对于基元,它们的值将被复制。否则,将复制对对象的引用。这就是
It's the same case with Object.assign() and Object spread operators
部分是指。
关于
The above code sample works just the same as if you'd copied the array in a to b using the .slice() method
...确实如此。这是 a.slice()
或 [].concat(a)
的更简洁的写法。除了一个相当大的例外。 ES6 剩余运算符(以及 Array.from(a)
)对所有可迭代对象同样有效,而不仅仅是数组。
对于对象的 深度 副本 ES6 没有提供任何新内容,对象(数组就是)应该手动递归复制。为了解决所有问题,使用经过验证的第三方辅助函数仍然有意义,例如 Lodash cloneDeep
.
伙计,程序员真的很不擅长显示实际显示差异的示例。
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]
的对象被 引用 到同一个对象,而不是 copy。然而 [1]
值不同意味着它确实是 copy。
浅拷贝表示第一层复制,深层次引用.