美文网首页
对象拷贝

对象拷贝

作者: 行走的蛋白质 | 来源:发表于2021-04-09 10:06 被阅读0次

    一. 深拷贝 / 浅拷贝

    1. 浅拷贝: 指针拷贝, 让拷贝前和拷贝后对象的指针指向同一块内存地址
    • 增加了原对象的引用计数
    • 没有新的内存分配
    let obj1 = {
        a: 1,
        b: 2
    }
    let obj2 = obj1
    obj1.a = 3
    console.log(obj2.a) // 3
    
    2. 深拷贝
    • Object.assign(),只能拷贝一层, 且会改变原有对象 - ( 数组 concat 方法不会改变原有数组 )
    • ES6 扩展运算符 { ...obj },同样只能拷贝一层
    • immutable.js 实现,利用其不可变数据集的特性,省去深拷贝环节每次改变只影响当前节点和它的父节点, 其他节点复用,效率高
    • lodash 库的 cloneDeep 方法实现深拷贝
    • 遍历 for in / Object.keys(obj) 递归, 代码以及优缺点如下:
    1). JSON.parse(JSON.stringify()) 实现深拷贝的不足
      1. 如果 obj 里面有时间对象,则 JSON.stringify 后再 JSON.parse 的结果,时间将只是字符串的形式。而不是时间对象;
    const obj = {
      date: new Date()
    }
    const newObj = JSON.parse(JSON.stringify(obj))
    console.log(Object.prototype.toString.call(obj.date)) // [object Date]
    console.log(Object.prototype.toString.call(newObj.date)) // [object String]
    console.log(newObj.date) // 2021-04-09T02:12:31.440Z
    
      1. 如果 obj 里有 RegExp、Error 对象,则序列化的结果将只得到空对象;
    const obj = {
      date: new Date(),
      regExp: new RegExp('\d'),
      error: new Error('error')
    }
    const newObj = JSON.parse(JSON.stringify(obj))
    console.log(newObj.regExp) // {}
    console.log(newObj.error) // {}
    
      1. 如果 obj 键名有 Symbol 或者值里有函数,undefined,则序列化的结果会丢失;
    const obj = {
      date: new Date(),
      regExp: new RegExp('\d'),
      error: new Error('error'),
      fn: function() {
        console.log('function')
      },
      und: undefined
    }
    const sym = Symbol('sym')
    test[sym] = 'sym'
    const newObj = JSON.parse(JSON.stringify(obj))
    console.log(newObj) // {date: "2021-04-10T01:47:04.806Z", regExp: {…}, error: {…}}
    
      1. 如果 obj 里有 NaN、Infinity 和 -Infinity,则序列化的结果会变成 null
    const obj = {
      date: new Date(),
      regExp: new RegExp('\d'),
      error: new Error('error'),
      fn: function() {
        console.log('function')
      },
      und: undefined,
      nan: NaN,
      infi: Infinity
    }
    const newObj = JSON.parse(JSON.stringify(obj))
    console.log(Object.prototype.toString.call(obj.date))
    console.log(Object.prototype.toString.call(newObj.date))
    console.log(newObj.nan) // null
    console.log(newObj.infi) // null
    
      1. JSON.stringify() 只能序列化对象的可枚举的自有属性,例如 如果 obj 中的对象是有构造函数生成的, 则使用 JSON.parse(JSON.stringify(obj)) 深拷贝后,会丢弃对象的 constructor;
    function Person(name) {
      this.name = name
    }
    const zs = new Person('zhangsan')
    const obj = {
      date: new Date(),
      regExp: new RegExp('\d'),
      error: new Error('error'),
      fn: function() {
        console.log('function')
      },
      und: undefined,
      nan: NaN,
      infi: Infinity,
      zs
    }
    const newObj = JSON.parse(JSON.stringify(obj))
    console.log(obj.zs)
    console.log(newObj.zs)
    
    compare
      1. 如果对象中存在循环引用的情况也无法正确实现深拷贝;
    对象循环引用
    2). Reflect 递归实现
    function deepClone(obj) {
        // 判断如果 obj 是基本类型数据或者函数, 直接返回
        if (!obj || typeof obj !== 'object') return obj;
        if (obj instanceof Error) {
            return new Error(obj);
        }
        if (obj instanceof RegExp) {
            return new RegExp(obj);
        }
        if (obj instanceof Date) {
            return new Date(obj);
        }
        const result = Array.isArray(obj) ? [] : {};
        Reflect.ownKeys(obj).forEach(item => {
            if (obj[item] && typeof obj[item] === 'object') {
                result[item] = deepClone(obj[item]);
            } else {
                result[item] = obj[item];
            }
        })
        return result;
    }
    // 测试
    const a = '111'
    console.log(deepClone(a))
    const test = {
        num: 0,
        str: '',
        boolean: true,
        unf: undefined,
        nul: null,
        obj: {
            name: '我是一个对象',
            num: 1,
            id: 1
        },
        arr: [0, 1, 2],
        func: function() {
            console.log('我是一个函数')
        },
        date: new Date(0),
        reg: new RegExp('我是一个正则', 'ig'),
        err: new Error('error')
    }
    const sym = Symbol('我是一个Symbol')
    test[sym] = 'Symbol'
    console.log(deepClone(test))
    console.log(deepClone(test.date))
    console.log(deepClone(test.reg))
    console.log(deepClone(test.err))
    
    Reflect_deepClone_result
    • 递归方法深拷贝互相引用的对象会造成栈溢出

    相关文章

      网友评论

          本文标题:对象拷贝

          本文链接:https://www.haomeiwen.com/subject/kahikltx.html