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 等。
    • 修改拷贝对象的第一层属性不会影响原对象,但修改嵌套对象的属性会影响原对象和拷贝对象。
相关推荐
勿语&30 分钟前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈32 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水2 小时前
简洁之道 - React Hook Form
前端
正小安4 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
小飞猪Jay5 小时前
C++面试速通宝典——13
jvm·c++·面试
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery