JS深拷贝及面试时手搓源码

前言

在JavaScript中,深拷贝是指在拷贝对象的同时,创建一个新的对象,并且递归地复制原对象中的所有嵌套对象和引用类型。这样,新对象和原对象的所有层级都是相互独立的,修改一个对象的属性不会影响另一个对象。在上篇文章中我们讲了浅拷贝,今天我们来聊聊JavaScript中的深拷贝,看看它与浅拷贝有什么不同,并且我们来手搓一个深拷贝源码,在前端面试中容易被问到。

深拷贝和浅拷贝是两种不同的对象复制方式,主要区别在于复制对象时是否递归复制嵌套对象及其引用。

  1. 浅拷贝:

    • 只复制对象的第一层结构,不会递归复制嵌套对象,嵌套对象仍然是原对象和拷贝对象共享的。
    • 浅拷贝方法包括 Object.assign、数组的 slice、concat 等。
    • 修改拷贝对象的第一层属性不会影响原对象,但修改嵌套对象的属性会影响原对象和拷贝对象。

    示例代码:

    js 复制代码
    let obj = { a: 1, b: { c: 2 } };
    let shallowCopy = Object.assign({}, obj);
    
    console.log(shallowCopy); // { a: 1, b: { c: 2 } }
    shallowCopy.b.c = 3;
    console.log(obj.b.c); // 3
  2. 深拷贝:

    • 递归复制整个对象结构,包括嵌套对象及其引用,生成一个完全独立的新对象,修改拷贝对象不会影响原对象。

    • 深拷贝的实现通常使用递归和循环来处理对象的每一层,防止循环引用导致的无限递归。

    • 深拷贝的实现可能相对复杂,但可以确保生成的对象与原对象完全独立。

在JS中,因为浅拷贝只复制对象的第一层结构,而不会递归复制嵌套对象并且还是引用嵌套对象,当我们修改嵌套对象中的属性值时,例如此题中的shallowCopy.b.c = 3,修改新对象时,原对象中的属性值还是会改变。

而深拷贝递归复制整个对象结构,包括嵌套对象及其引用,生成一个完全独立的新对象,也就是说,如果原对象的对象里面还有嵌套的对象,但是深拷贝都会将嵌套的对象复制过去,而不是像浅拷贝一样去引用嵌套的对象,假设shallowCopy是我们深拷贝obj创造出的对象,当我们像这样shallowCopy.b.c = 3去修改对象中嵌套的对象的属性时,对应的对象还是不会改变,这就是深拷贝。

深拷贝实现

通过上篇文章,我们知道了常见的实现浅拷贝的方法,如slice concat assign等等,那js有没有提供实现深拷贝的方法呢?

其实js是有提供实现深拷贝的方法的,那么就是JSON.parse(JSON.stringify(obj)),在讲这个方法之前,我们先来搞懂JSON.stringifyJSON.parse

JSON.stringifyJSON.parse 是 JavaScript 中用于处理 JSON 数据的两个核心方法。

  1. JSON.stringify:

    • 作用:将 JavaScript 对象或值转换为 JSON 字符串。

    • 语法:JSON.stringify(value[, replacer[, space]])

    • 参数:

      • value: 要转换为 JSON 字符串的值。
      • replacer (可选):用于转换结果的函数或数组。
      • space (可选):用于控制缩进的字符串或数字。
    • 返回值:表示给定值的 JSON 字符串。

    js 复制代码
    let obj = { name: 'John', age: 30 };
    let jsonString = JSON.stringify(obj);
    console.log(jsonString); // '{"name":"John","age":30}'
  2. JSON.parse:

    • 作用:将 JSON 字符串解析为 JavaScript 对象。

    • 语法:JSON.parse(text[, reviver])

    • 参数:

      • text: 要解析的 JSON 字符串。
      • reviver (可选):用于在解析期间对结果执行转换的函数。
    • 返回值:解析得到的 JavaScript 对象。

    js 复制代码
    let jsonString = '{"name":"John","age":30}';
    let parsedObj = JSON.parse(jsonString);
    console.log(parsedObj); // { name: 'John', age: 30 }

JSON.parse(JSON.stringify(obj))

使用 JSON.parse(JSON.stringify(obj)) 进行深拷贝是一种常见的简单方法。这方法适用于对象中只包含原始类型数据(如字符串、数字、布尔值、null)以及没有循环引用的情况。这种方法的优点是简单易懂,缺点是无法处理包含函数、正则表达式、undefined 等特殊类型的对象。

示例代码:

js 复制代码
let obj = { a: 1, b: { c: 2 } };

let deepCopyObj = JSON.parse(JSON.stringify(obj));

console.log(deepCopyObj); // { a: 1, b: { c: 2 } }
deepCopyObj.b.c = 3;
console.log(obj.b.c); // 2

需要注意的是:

  1. 无法处理特殊类型: JSON.stringify() 会忽略对象属性中的函数、正则表达式、undefined 等,因为这些类型在 JSON 中没有对应的表示。在还原时,这些特殊类型将被忽略或转为 null。
  2. 循环引用问题: 如果对象存在循环引用,即对象属性之间形成一个循环,JSON.stringify() 无法处理,会抛出错误。

手搓源码

js 复制代码
function deepCopy(obj) {
    let objCopy = {}
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (obj[key] instanceof Object) {   // obj[key]是引用类型
                objCopy[key] = deepCopy(obj[key])
            } else {
                objCopy[key] = obj[key]
            }
       }
    }
    return objCopy
}
  1. deepCopy 函数接收一个对象 obj 作为参数。
  2. let objCopy = {} 创建了一个新的空对象,用于存储深拷贝的结果。
  3. for (let key in obj) 遍历对象 obj 的所有属性。
  4. if (obj.hasOwnProperty(key)) 判断属性是否是对象自身的属性,而不是继承自原型链上的属性。
  5. if (obj[key] instanceof Object) 判断属性值是否是引用类型(Object 类型)。如果是引用类型,就递归调用 deepCopy 对其进行深拷贝,然后将拷贝结果赋值给 objCopy[key]
  6. 如果属性值不是引用类型,直接将其赋值给 objCopy[key]
  7. 最终返回深拷贝的结果 objCopy

总结

1.深拷贝:

  • 递归复制整个对象结构,包括嵌套对象及其引用,生成一个完全独立的新对象,修改拷贝对象不会影响原对象。 - 深拷贝的实现通常使用递归和循环来处理对象的每一层,防止循环引用导致的无限递归。 - 深拷贝的实现可能相对复杂,但可以确保生成的对象与原对象完全独立。
  1. 浅拷贝:

    • 只复制对象的第一层结构,不会递归复制嵌套对象,嵌套对象仍然是原对象和拷贝对象共享的。
    • 浅拷贝方法包括 Object.assign、数组的 slice、concat 等。
    • 修改拷贝对象的第一层属性不会影响原对象,但修改嵌套对象的属性会影响原对象和拷贝对象。
相关推荐
bloxed8 分钟前
前端文件下载多方式集合
前端·filedownload
余生H14 分钟前
前端Python应用指南(三)Django vs Flask:哪种框架适合构建你的下一个Web应用?
前端·python·django
LUwantAC22 分钟前
CSS(四)display和float
前端·css
cwtlw26 分钟前
CSS学习记录20
前端·css·笔记·学习
界面开发小八哥31 分钟前
「Java EE开发指南」如何用MyEclipse构建一个Web项目?(一)
java·前端·ide·java-ee·myeclipse
谢道韫66638 分钟前
今日总结 2024-12-24
javascript·vue.js·elementui
一朵好运莲40 分钟前
React引入Echart水球图
开发语言·javascript·ecmascript
米奇妙妙wuu1 小时前
react使用sse流实现chat大模型问答,补充css样式
前端·css·react.js
傻小胖1 小时前
React 生命周期完整指南
前端·react.js
梦境之冢1 小时前
axios 常见的content-type、responseType有哪些?
前端·javascript·http