javascript 为什么扩展元素不适合复制多维数组?

ykejflvf  于 2023-05-16  发布在  Java
关注(0)|答案(5)|浏览(98)

来自mdn:Spread Syntax
注意:通常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和事情仍然按预期工作。
那么,为什么spread语法不适合复制多维数组呢?
我很感激任何帮助。
编辑:
阅读estus和vol7ron的答案帮助我弄清楚了事情。基本上,正如estus指出的那样,技术上只有数组内部的数组,而不是多维数组。
正如vol7ron所解释的那样,只有数组的第一层被复制,因此内存中的对象对于任何进一步嵌套的元素都保持不变。
我还错误地怀疑使用spread语法的行为应该与slice操作符有任何不同

wlp8pajw

wlp8pajw1#

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

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]的值不同,这意味着它确实是一个 * 副本 *。

  • 浅拷贝 * 意味着第一级被 * 拷贝 *,更深的级别被 * 引用 *。
2guxujil

2guxujil2#

数组是对象,[...a]创建a数组对象的副本。
对于语言本身来说,没有多维数组--一个数组中有另一个数组。包含数组、普通对象、函数还是原语都没有关系。对于基元,它们的值将被复制。否则,将复制对对象的引用。这是什么
这与Object.assign()和Objectspread操作符的情况相同
部分指。
关于
上面的代码示例的工作原理与使用.slice()方法将a中的数组复制到b中一样
确实如此这是写a.slice()[].concat(a)的更简洁的方法。但有一个例外。ES6 rest操作符(以及Array.from(a))对所有可迭代对象都同样有效,而不仅仅是数组。
对于对象的deep副本,ES6没有提供任何新的内容,对象(数组)应该手动递归复制。为了解决所有问题,使用经过验证的第三方帮助函数仍然是有意义的,例如Lodash cloneDeep

dced5bon

dced5bon3#

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

// 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
nkhmeac6

nkhmeac64#

因此,这个例子试图传达的是,var b = [...a];不会展开a的内部数组(例如b = [1,2,3]),而是b将是[[1],[2],[3]]。所以b.shift()删除并返回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] ]

kq0g1dla

kq0g1dla5#

JS Vanilla中有一个structuredClone()函数。请查看以下地址的文档:https://developer.mozilla.org/en-US/docs/Web/API/structuredClone。这可用于创建深层副本。

相关问题