> 文章列表 > 【面试题】 JavaScript 中的深浅拷贝: 原理与实现

【面试题】 JavaScript 中的深浅拷贝: 原理与实现

【面试题】 JavaScript 中的深浅拷贝: 原理与实现

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全

前言

  在开发过程中,我们经常会遇到需要复制一个对象数组的情况。在 JavaScript 中,我们可以使用浅拷贝或深拷贝来实现复制功能。浅拷贝只会复制对象或数组的第一层属性,如果属性的值还是对象或数组,那么它们之间的引用关系并不会改变。相反,深拷贝会完全复制对象或数组的所有属性,并创建新的引用关系。在什么情况下需要使用深拷贝呢?通常来说,当我们希望对象或数组的改变不影响原对象或数组时,就需要使用深拷贝。例如,我们可能希望在处理数据的过程中保留原始数据的副本,或者在修改一个对象的属性时不影响原对象。在本文中,我们将介绍 JavaScript 中的深拷贝方法,并给出使用深拷贝的示例代码。复制代码

1.深浅拷贝究竟是什么东西???

  在 JavaScript 中,对象和数组都是引用类型,它们的值是存储在内存中的地址,而不是实际的值。当我们复制一个对象或数组时,如果我们只是复制它们的地址,那么原来的对象或数组和新的对象或数组都指向同一个地址,对其中一个对象或数组的修改会影响另一个对象或数组。这种复制方式称为浅拷贝。为了避免这种情况,我们需要进行深拷贝,即复制对象或数组的值,而不是复制地址。
复制代码

2.深拷贝的实现

 深拷贝的方法有很多种,常见的方法包括使用 `JSON.parse` 和 `JSON.stringify`、使用递归算法、使用 lodash 等。
复制代码

2.1 使用JSON.parse 和 JSON.stringify 进行深拷贝

使用 JSON.parse 和 JSON.stringify 进行深拷贝的方法非常简单,只需要先将要拷贝的对象或数组使用 JSON.stringify 转化为字符串,然后再使用 JSON.parse 将字符串转回原来的对象或数组即可。

下面是一个简单的示例,展示了如何使用 JSON.parse 和 JSON.stringify 进行深拷贝:

const original = { a: 1, b: { c: 2 } };// 使用 JSON.stringify 将对象转化为字符串,再使用 JSON.parse 将字符串转回对象
const copy = JSON.parse(JSON.stringify(original));console.log(original === copy); // false
console.log(original.b === copy.b); // false
复制代码

在这个示例中,我们先定义了一个包含嵌套对象的对象 original,然后使用 JSON.parse 和 JSON.stringify 进行深拷贝,得到了新的对象 copy。最后,我们使用 === 运算符比较了两个对象是否相等,以及两个对象的嵌套对象是否相等,发现了两个对象和两个嵌套对象都不相等,说明进行了深拷贝。

2.2 使用递归算法实现深拷贝

  使用 `JSON.parse` 和 `JSON.stringify` 进行深拷贝也有一些限制,比如不能复制函数、Symbol 等类型的值,也不能正确地复制循环引用的对象。因此,在实际应用中,我们还需要考虑使用其他的深拷贝方法来解决这些限制,例如递归算法实现深拷贝,它可以递归地复制对象或数组的值,并判断是否为基本类型(如数字、字符串、布尔值等),如果是基本类型就直接返回,否则就继续递归复制。
复制代码

下面是一个使用递归算法实现深拷贝的示例:

function deepCopy(obj) {if (typeof obj !== 'object' || obj === null) {return obj;}// 判断是数组还是对象const isArray = Array.isArray(obj);const copy = isArray ? [] : {};for (const key in obj) {if (obj.hasOwnProperty(key)) {copy[key] = deepCopy(obj[key]);}}return copy;
}const original = { a: 1, b: { c: 2 } };
const copy = deepCopy(original);console.log(original === copy); // false
console.log(original.b === copy.b); // false
复制代码

在这个示例中,我们定义了一个名为 deepCopy 的函数,它接受一个对象或数组作为参数,并进行深拷贝。首先,我们使用 typeof 运算符判断传入的参数是否为对象或数组,如果不是就直接返回。然后,我们使用 Array.isArray 函数判断传入的参数是数组还是对象,并创建新的数组或对象。最后,我们使用循环遍历对象或数组的属性,并使用递归调用 deepCopy 函数来复制属性的值。

使用递归算法实现深拷贝的方法相对来说比较复杂,但是它有一个明显的优势,就是可以正确地复制函数、Symbol 等类型的值,以及循环引用的对象。

2.2 使用深拷贝时该注意什么?

  1. 与原始数据的类型相关的问题

在使用深拷贝时,我们要注意,有些类型的数据在被深拷贝后可能会发生变化。例如,使用 JSON.parse() 和 JSON.stringify() 函数时,会把函数转换成字符串,而使用递归函数时,会把正则表达式转换成空对象。

  1. 与浅拷贝相关的问题

在某些情况下,我们希望对象或数组的某些属性不被深拷贝,而是使用浅拷贝。这可以通过自定义递归函数来实现。例如,可以设置一个黑名单,表示哪些属性不需要深拷贝。

3. 浅拷贝的实现

浅拷贝是指在复制对象或数组时,只复制它的第一层属性,而不会复制它的属性的值(如果属性的值还是对象或数组的话)。因此,浅拷贝得到的新对象或数组与原对象或数组之间仍然存在引用关系

在 JavaScript 中,我们可以使用下面几种方法来实现浅拷贝:

  • 使用展开运算符(...):let newArray = [...oldArray]let newObject = {...oldObject}
  • 使用 Object.assign() 函数:let newObject = Object.assign({}, oldObject)
  • 使用数组的 slice() 函数或对象的 Object.keys() 函数:let newArray = oldArray.slice()let newObject = Object.keys(oldObject).reduce((acc, key) => ({...acc, [key]: oldObject[key]}), {})

3.1 浅拷贝的应用场景

  与深拷贝相比,浅拷贝的实现方法要简单得多,但是它的功能也相对较弱。如果我们希望复制对象或数组的所有属性,并创建新的引用关系,还是需要使用深拷贝,但是在某些情况下,我们可能希望使用浅拷贝。
复制代码
  • 当我们需要构建一个新的对象或数组时,可能希望使用浅拷贝。因为浅拷贝只复制第一层属性,所以可以节省拷贝的时间和空间。例如,我们可以使用浅拷贝来创建一个新的数组,其中包含原数组的所有元素,但是不包含原数组的任何属性。

  • 当我们希望复制对象或数组的部分属性时,也可以使用浅拷贝。例如,我们可以使用展开运算符或 Object.assign() 函数来复制对象的某些属性。这可以节省拷贝的时间和空间,因为我们只复制了需要的属性。

总之,浅拷贝在一些特定的场景下非常有用,但是它的功能也相对较弱。我们应该根据实际情况选择使用浅拷贝

3.2 浅拷贝的实现方法

下面我们列举几种常见的方法:

  1. 使用展开运算符(...):
function shallowClone(obj) {return {...obj};
}
复制代码
  1. 使用 Object.assign() 函数:
function shallowClone(obj) {return Object.assign({}, obj);
}
复制代码
  1. 使用内置构造函数:
function shallowClone(obj) {if (obj === null || typeof obj !== 'object') {return obj;}let clone = new obj.constructor();for (let key in obj) {clone[key] = obj[key];}return clone;
}let obj = {a: 1, b: 2, c: [3, 4, 5]};
let shallow = shallowClone(obj);
console.log(shallow); // {a: 1, b: 2, c: [3, 4, 5]}
复制代码

4.总结

最后,在使用深浅拷贝时,我们要根据实际的需求来决定使用哪种方法,并确保复制的对象或数组能够满足我们的需求。
复制代码

如有帮助,麻烦点个赞,如有错误请指出,我是CoderBug,一个跟你一样追风的少年!

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全