Javascript 中的浅拷贝深拷贝是什么?一文带你搞懂,不再犯错

Posted 前端修罗场

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Javascript 中的浅拷贝深拷贝是什么?一文带你搞懂,不再犯错相关的知识,希望对你有一定的参考价值。

⭐️ 本文首发自 前端修罗场,是一个由 资深开发者 独立运行 的专业技术社区,我专注 Web 技术、模拟面试、简历修改、Web3、区块链以及职业发展。创作的 《前端面试复习笔记》(点击阅读原文订阅),广受好评,已帮助多位同学提升实力、拿到 offer。现在订阅,私聊博主即可获取一次 免费的模拟面试/简历指导服务,帮你评估知识点的掌握程度,获得更全面的学习指导意见!

浅拷贝和深拷贝是javascript中抛出的术语,如果你以前没有听说过,可能会感到困惑。我们经常听说,像 slicefilter 这样的数组方法会对原始数组进行浅层复制

一个数组或对象的浅层拷贝是指它们在内存中都有相同的引用。这意味着,如果你 改变了浅层拷贝,它也可能改变原始拷贝。我说可能,因为情况并不总是这样的。

让我们看一个使用 slice的例子。

let arrayOne = [ '⚡️', '🔎', '🔑', '🔩' ];
let arrayOneSlice = arrayOne.slice(2, 3);  

console.log(arrayOne); // [ '⚡️', '🔎', '🔑', '🔩' ]
console.log(arrayOneSlice); // [ '🔑' ]

这里我们有一个数组,然后我们在变量 arrayOneSlice 中进行 slice 操作。这两个数组在内存中都有相同的引用,因为 slice 对它们做了一个浅层拷贝。所以如果我们试图更新 arrayOneSlice ,它也会影响 arrayOne ,对吗?

let arrayOne = [ '⚡️', '🔎', '🔑', '🔩' ];
let arrayOneSlice = arrayOne.slice(2, 3);  

// 更新 arrayOneSlice
arrayOneSlice[2] = '⚡️'

console.log(arrayOne); // [ '⚡️', '🔎', '🔑', '🔩' ]
console.log(arrayOneSlice); // [ '🔑', empty, '⚡️' ]

因为我们使用了方括号符号,Javascript把它解释为把一个新的值放到 [2] 的位置。所以只有 arrayOneSlice 被更新了。虽然 '🔑'arrayOne 中处于 [2] 的位置,但它在 arrayOneSlice 中处于 [0] 的位置。这可能会给人一种错觉,认为这两个数组是复制的,并且相互独立。 但事实也并非如此。考虑一下下面的例子。

let arrayOne = [  items: [ '🔎' ], '🔎', '🔑', '🔩' ];
let arrayOneSlice = arrayOne.slice(0, 3);  

// 更新 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的众多怪癖之一。了解它们是什么,可以在将来调试时省去很多麻烦,而使用深度拷贝是避免这些问题的一个好办法。

以上是关于Javascript 中的浅拷贝深拷贝是什么?一文带你搞懂,不再犯错的主要内容,如果未能解决你的问题,请参考以下文章

Javascript中的浅拷贝和深拷贝

JavaScript中的浅拷贝和深拷贝

javascript中的浅拷贝ShallowCopy与深拷贝DeepCopy

javascript中的浅拷贝和深拷贝

JavaScript数组对象的浅拷贝与深拷贝深浅拷贝的区别+图解原理

JavaScript学习(七十八)—实现对数据的浅拷贝和深拷贝