当前位置: 首页 > article >正文

js中的深拷贝与浅拷贝 手写深拷贝代码

1 什么是深拷贝和浅拷贝?

        深拷贝和浅拷贝都是复制对象时常用的两种方式,区别在于对于嵌套对象的处理,浅拷贝只复制属性的第一层属性,双方修改嵌套对象将会互相影响。深拷贝会递归复制每一层的属性,修改任意一方互不影响。

2 浅拷贝和浅拷贝的实现方法

2.1 浅拷贝的实现方法

        1.使用Object.assign()方法,接收两个参数(target, source),将source上的第一层属性复制到target的第一层属性上(不改变原有属性,如果重名将会被覆盖,常用于配置对象的修改)。

// Object.assign
// Object.assign() copies property values from a source object to a target object. For example, consider the following code:
const source = { b: 4, c: {d : 9} };
const shallowCopy = Object.assign({}, source);

shallowCopy.c.d = 10;
console.log(source); // { b: 4, c: { d: 10 } }

         2. 使用展开运算符赋值

// use ... (spread operator) for shallow copy
const source = { b: 4, c: {d : 9} };
const shallowCopy = {...source};

shallowCopy.c.d = 10;
console.log(source); // { b: 4, c: { d: 10 } }

        3. 对数组可以使用slice或者concat方法

// Array use slice or concat for shallow copy
const source = [1, 2, 3];
const shallowCopy_01 = source.slice();
// or
const shallowCopy_02 = source.concat();
shallowCopy_01[0] = 100;
shallowCopy_02[0] = 100;
console.log(source); // [1, 2, 3]

2.2 深拷贝的实现方法-包含手写实现

        方法一:使用递归手写实现,具体实现步骤如下所示:

        1. 处理基本类型和引用类型

function deepClone(value){
    // 处理基本类型(非引用类型)和null
    if(typeof value !== 'object' || value === null){
        return value
    }
}

        2.  处理日期Date对象和正则RegExp对象

// 处理正则对象
function deepClone(value){
    // ...上一步代码

    // Date对象和正则对象直接通过new的方式创建一个新的对象并返回
    if(value instanceof RegExp){
        return new RegExp(value)
    }
    if(value instanceof Date){
        return new Date(value)
    }
}

         3. 处理函数(函数一般不进行深拷贝,闭包所设计的引用过于复杂)

function deepClone(value){
    //...前面代码    

    // 处理函数 直接返回,不做处理
    if(typeof value === 'function'){
        return value
    }
}

        4.  初始化拷贝对象

// 初始化拷贝对象
function deepClone(value){
    // ...前面代码

    // 初始化拷贝对象
    const copy = Array.isArray(value) ? [] : {}
}

        5.  处理循环引用:使用WeakMap记录拷贝过的对象,防止循环引用

// 处理循环引用
function deepClone(value, cache = new WeakMap()){
    //...前面代码

    // 如果拷贝过的对象,直接返回
    if(cache.get(value)){
        return cache.get(value)
    }
    // 缓存拷贝对象
    cache.set(value, copy)
}

        6.  递归拷贝对象的属性:使用 Reflect.ownKeys 获取对象的所有属性,包括不可枚举属性和 Symbol 属性,然后递归地拷贝每个属性:

// 递归拷贝对象属性
function deepClone(value, cache = new WeakMap()){
    // ...上面的代码

    // 递归拷贝对象属性
    const keys = Reflect.ownKeys(value)
    for(let key of keys){
        copy[key] = deepClone(value[key], cache)
    }
    return copy
}

        7. 处理 Map和Set

function deepClone(value, cache = new WeakMap()){
    // ...上面代码
    // 缓存拷贝对象
    cache.set(value, copy)

    // 处理Map和Set
    if(value instanceof Map){
        const copy = new Map()
        value.forEach((val, key) => {
            copy.set(key, deepClone(val, cache))
        })
        return copy
    }

    if(value instanceof Set){
        const copy = new Set()
        value.forEach(val => {
            copy.add(deepClone(val, cache))
        })
        return copy
    }

    // ...后续代码
}
手写深拷贝完整代码
function deepClone(value, cache = new WeakMap()){
    // 处理基本类型(非引用类型)和null,直接返回即可
    if(typeof value !== 'object' || value === null){
        return value
    }

    // Date对象和正则对象直接通过new的方式创建一个新的对象并返回
    if(value instanceof RegExp){
        return new RegExp(value)
    }
    if(value instanceof Date){
        return new Date(value)
    }

    // 处理函数 直接返回,不做处理
    if(typeof value === 'function'){
        return value
    }

    // 初始化拷贝对象(保留对象原型链)
    const copy = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value))

    // 如果拷贝过的对象,直接返回
    if(cache.get(value)){
        return cache.get(value)
    }
    // 缓存拷贝对象
    cache.set(value, copy)

    // 处理Map和Set
    if(value instanceof Map){
        const copy = new Map()
        value.forEach((val, key) => {
            copy.set(key, deepClone(val, cache))
        })
        return copy
    }

    if(value instanceof Set){
        const copy = new Set()
        value.forEach(val => {
            copy.add(deepClone(val, cache))
        })
        return copy
    }

    // 递归拷贝对象属性(包含不可枚举属性)
    const keys = Reflect.ownKeys(value)
    for(let key of keys){
        copy[key] = deepClone(value[key], cache)
    }

    return copy
}

         方法二:使用JSON.parse(JSON.stringify(obj)),但是会造成部分数据丢失,且无法处理特殊对象。

// deep copt use Json.parse and Json.stringify
const source = { b: 4, c: {d : 9} };
const deepCopy = JSON.parse(JSON.stringify(source));
深拷贝测试示例
const obj = {
  num: 1,
  str: 'string',
  bool: true,
  nullValue: null,
  undefinedValue: undefined,
  symbol: Symbol('sym'),
  date: new Date(),
  regExp: /\w+/g,
  func: function () { console.log('function'); },
  arr: [1, 2, { a: 3 }],
  obj: { x: 10, y: { z: 20 } },
  map: new Map([['key1', 'value1'], ['key2', { a: 1 }]]),
  set: new Set([1, 2, { b: 3 }]),
  [Symbol('symbolKey')]: 'symbolValue',
};

obj.circularRef = obj; // 添加循环引用

const clonedObj = deepClone(obj);

console.log(clonedObj);

        测试结果如下所示:

 

3 深拷贝需要注意的问题

        实现深拷贝时需要考虑以下问题:

  • 循环引用问题:如果对象内部引用自己,不考虑该情况将会导致无限递归。
  • 性能损耗:深拷贝大型对象和数组将会浪费大量性能,带来卡顿。
  • 考虑特殊对象类型:例如Date RegExp Set Map Function等对象需要特殊处理
  • 不可枚举属性和原型链:只会复制对象的可枚举属性
  • 某些深拷贝造成的数据丢失:使用JSON.parse(JSON.stringify(obj))时造成undefined Symbol Funcion等类型数据的丢失

http://www.kler.cn/a/326155.html

相关文章:

  • 黑马智数Day10
  • 基于Python的仓库管理系统设计与实现
  • 模糊神经网络学习方法探讨
  • 【Fargo】23:采集时间转rtp时间
  • 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--完善TODO标记的代码
  • Py之pymupdf:基于langchain框架结合pymupdf库实现输出每个PDF页面的文本内容、元数据等
  • 深入剖析 Android Lifecycle:构建高效稳定的应用
  • 如何设计能吸引下载的截图以及注意事项
  • SpringBoot助力墙绘艺术市场创新
  • golang学习笔记16-数组
  • java 解析excel (本地资源)
  • Android常用C++特性之std::find_if
  • CF1619D.New Year‘s Problem
  • 解决 TypeError: Expected state_dict to be dict-like, , got <class ‘*‘>.
  • Acwing 最小生成树
  • 每日OJ题_牛客_NC40链表相加(二)_链表+高精度加法_C++_Java
  • 《黑神话:悟空》天命人速通法宝 | 北通鲲鹏20智控游戏手柄评测
  • linux打开桌面软件(wps)、获取已打开的文件名(wps)
  • Ini文件读写配置工具类 - C#小函数类推荐
  • 汽车免拆诊断案例 | 2016 款宾利GT车仪表盘上的多个故障灯点亮
  • 使用TensorFlow实现一个简单的神经网络:从入门到精通
  • 动手学深度学习(李沐)PyTorch 第 3 章 线性神经网络
  • TiDB 性能测试的几个优化点
  • Leetcode热题100-438 找出字符串中所有字母异位数
  • R语言非参数回归预测摩托车事故、收入数据:局部回归、核回归、LOESS可视化...
  • 408算法题leetcode--第19天