前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >再说深拷贝对象的研坑

再说深拷贝对象的研坑

作者头像
张炳
发布2020-05-11 17:31:23
5010
发布2020-05-11 17:31:23
举报

浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。 深拷贝:会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

浅拷贝的实现方式

直接赋值一个变量

代码语言:javascript
复制
  let obj = {username: 'kobe', age: 39, sex: {option1: '男', option2: '女'}};
  let obj1 = obj;
  obj1.sex.option1 = '不男不女'; // 修改复制的对象会影响原对象
  console.log(obj1, obj);
alt text
alt text

Object.assign()

代码语言:javascript
复制
let obj = {
    username: 'kobe'
    };
let obj2 = Object.assign(obj);
obj.username = 'wade';
console.log(obj2);//{username: "wade"}

Array.prototype.concat()

代码语言:javascript
复制
let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);

修改新对象会改到原对象:

alt text
alt text

Array.prototype.slice()

代码语言:javascript
复制
let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

同样修改新对象会改到原对象:

alt text
alt text

关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。详细规则请看MDN对应函数讲解。

深拷贝的实现方式

只有值类型数据的深拷贝

针对只有值的数据对象,下面一行代码足以!

代码语言:javascript
复制
JSON.parse(JSON.stringify(obj))

原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。这种方法只能处理只有值类型数据的拷贝。

不严谨的简单的深拷贝

代码语言:javascript
复制
function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
             //通过hasOwnProperty方法来进行筛选,所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
            if (typeof source[i] === 'object') {
                target[i] = clone(source[i]); // 注意这里
            } else {
                target[i] = source[i];
            }
        }
    }

    return target;
}

问题存在:

  • 没有对参数做检验
  • 判断是否对象的逻辑不够严谨
  • 没有考虑数组的兼容

进阶深拷贝

代码语言:javascript
复制
function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}

function deepCopy(obj)
{
    let tempObj = Array.isArray(obj) ? [] :{};
    for(let key in obj)
    {
        tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key];
    }
    return tempObj;
}

问题存在:

  • __拷贝环__,也就是对 对象循环引用 的拷贝出现问题

对象环知识点补充

方法一 用try catch的捕获异常的方法来判断,代码简洁

代码语言:javascript
复制
function cycleDetector (obj) {
    console.log(arguments)
  // 请添加代码
    let result = false;
    try {
        JSON.stringify(obj);
    } catch (e) {
        result = true;
    } finally {
        return result;
    }
}

方法二 时间更快,但是它执行递归,逻辑较第一种更复杂,空间也需要更大

代码语言:javascript
复制
function cycleDetector2(obj) {
    let hasCircle = false,
        cache = [];

    (function(obj) {
        Object.keys(obj).forEach(key => {
            const value = obj[key]
            if (typeof value == 'object' && value !== null) {
                const index = cache.indexOf(value)
                if (index !== -1) {
                    hasCircle = true
                    return
                } else {
                    cache.push(value)
                    arguments.callee(value)
                    // (注:箭头函数没有arguments对象,此时的arguments指向该匿名函数的参数)
                }
            }
        })
    })(obj)

    return hasCircle
}

针对环的深拷贝

可以使用一个WeakMap结构存储已经被拷贝的对象,每一次进行拷贝的时候就先向WeakMap查询该对象是否已经被拷贝,如果已经被拷贝则取出该对象并返回,将deepCopy函数改造成如下:

代码语言:javascript
复制
function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {    
    if(hash.has(obj)) return hash.get(obj)    
    let cloneObj = Array.isArray(obj) ? [] : {}
    hash.set(obj, cloneObj)    
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }   
    return cloneObj
}

问题存在:

  • 没有考虑对new Date(),正则,函数类型的对象的拷贝

结合环,针对date,reg,箭头函数类型的深拷贝

代码语言:javascript
复制
const obj = {    arr: [111, 222],    obj: {key: '对象'},    a: () => {console.log('函数')},    date: new Date(),    reg: /正则/ig}

function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {  
      let cloneObj;   
      let Constructor = obj.constructor; 
      switch(Constructor){       
        case RegExp:
            cloneObj = new Constructor(obj)         
            break;
        case Date:
            cloneObj = new Constructor(obj.getTime())           
            break;
        case Function:
            cloneObj = eval(obj.toString());
            break;
        default:           
         if(hash.has(obj)) return hash.get(obj)
            cloneObj = new Constructor()
            hash.set(obj, cloneObj)
      }   

      for (let key in obj) {
            cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
      }    
      return cloneObj;
}

const cloneObj = deepCopy(obj);
console.log(cloneObj);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 浅拷贝的实现方式
    • 直接赋值一个变量
      • Object.assign()
        • Array.prototype.concat()
          • Array.prototype.slice()
          • 深拷贝的实现方式
            • 只有值类型数据的深拷贝
              • 不严谨的简单的深拷贝
                • 进阶深拷贝
                  • 对象环知识点补充
                • 针对环的深拷贝
                  • 结合环,针对date,reg,箭头函数类型的深拷贝
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档