持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第27天,点击查看活动详情
浅拷贝和深拷贝是 Javascript 中出现的术语,如果您以前从未听说过它们,可能会感到困惑。经常听到像slice或filter这样的数组方法对原始数组进行浅拷贝。
JavaScript 中的浅拷贝是什么?
数组或对象的浅拷贝是它们在内存中具有相同的引用。这意味着如果您更改浅副本,它也可能更改原始副本。我说可能,因为情况并非总是如此。
让我们看一个例子slice
:
let arrayOne = [ '⚡️', '🔎', '🔑', '🔩' ];
let arrayOneSlice = arrayOne.slice(2, 3);
console.log(arrayOne); // [ '⚡️', '🔎', '🔑', '🔩' ]
console.log(arrayOneSlice); // [ '🔑' ]
这里我们有一个数组,然后我们slice
在变量中arrayOneSlice
。这两个数组在内存中具有相同的引用,因为slice
对它们进行了浅拷贝。所以如果我们尝试更新arrayOneSlice
,它也会影响arrayOne
,对吧?
let arrayOne = [ '⚡️', '🔎', '🔑', '🔩' ];
let arrayOneSlice = arrayOne.slice(2, 3);
// Update arrayOneSlice
arrayOneSlice[2] = '⚡️'
console.log(arrayOne); // [ '⚡️', '🔎', '🔑', '🔩' ]
console.log(arrayOneSlice); // [ '🔑', empty, '⚡️' ]
只是,它没有——因为我们使用了方括号表示法,Javascript 将其解释为将新值放入该[2]
位置。所以只有arrayOneSlice
更新了——而且有充分的理由。虽然 '🔑' 位于[2]
中arrayOne
,但它位于[0]
中arrayOneSlice
。这可能会让人产生这样的错觉,即这两个数组是副本并且彼此独立运行 - 但事实也并非如此。考虑以下示例:
let arrayOne = [ { items: [ '🔎' ]}, '🔎', '🔑', '🔩' ];
let arrayOneSlice = arrayOne.slice(0, 3);
// Update arrayOneSlice
arrayOneSlice[0].items = [ '⚡️' ]
console.log(arrayOne); // [ { items: [ '⚡️' ]}, '🔎', '🔑', '🔩' ]
console.log(arrayOneSlice); // [ { items: [ '⚡️' ]}, '🔎', '🔑' ]
在这里,我们更新arrayOneSlice[0].items
了 , 并且它在两个数组上都更新了,因为items
两个数组上的相同位置都存在,我们没有分配新值,而是使用点.
符号来更新现有属性。在 Javascript 中,这会更新我们使用slice
.
浅拷贝要记住的要点是,调整一个可能会影响您尝试复制的原件 - 内存中的引用是相同的,并且引用指向数组的值 - 所以你必须更加小心。您不想做的是创建意外行为,其中数组的原始和副本不会在您预期的时候同步更新。
那么如何在 Javascript 中进行深度复制呢?
Javascript 在深拷贝方面历来存在一些问题。Javascript 中的大多数方法三个点或展开法, Object.create()
, Object.assign()
, 和Array.from()
所有的浅拷贝。
但是,深层副本在内存中具有不同的引用,因此您不必担心在使用它们时会改变原始副本。当我们想避免这种情况时,这使它们非常有用。
可以通过序列化或自定义脚本进行深度复制,以将对象或数组的每个部分复制到新的部分中 - 在内存中创建新的引用。例如,这将在 Javascript 中创建一个带有新引用的新数组:
let myArray = [ 1, 2, 3, 4 ];
let deepCopy = JSON.parse(JSON.stringify(myArray));
不过,这不一定是最好的方法。您还可以使用该structuredClone()
函数进行深层复制:
let myArray = [ 1, 2, 3, 4 ];
let deepCopy = structuredClone(myArray);
现在我们已经创建了具有深层副本的新数组,我们不再需要担心在更改副本时会弄乱原始数组。
结论
浅拷贝非常令人困惑,并且是 Javascript 呈现的众多怪癖之一。了解它们是什么可以为您在将来调试时省去很多麻烦,而使用深拷贝是避免其中一些问题的好方法。