Javascript的浅拷贝

91 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 呈现的众多怪癖之一。了解它们是什么可以为您在将来调试时省去很多麻烦,而使用深拷贝是避免其中一些问题的好方法。