最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 面试官:老是问我深浅拷贝问题,自己看文章

    正文概述 掘金(人生代码)   2021-01-22   500

    欢迎各位大佬关注我的公众号

    《人生代码》

    面试官:老是问我深浅拷贝问题,自己看文章

    你如果出去面试,面试官老是会问你一些很神奇,但又不得不去了解的问题,今天这个问题就是其中之一,在工作中,其实我们经常使用对象拷贝,数组拷贝,但是往往会忽略了其中的原理,今天我们就来一探究竟。

    看这篇文章的开始,你需要带着两个问题:

    • 什么是深浅拷贝
    • 如何实现深浅拷贝

    什么是深浅拷贝

    借助ConardLi大佬以下两张图片,帮我们更好的理解两者的含义:

    面试官:老是问我深浅拷贝问题,自己看文章
    面试官:老是问我深浅拷贝问题,自己看文章

    从上面两张图,我们可以知道:

    浅拷贝:比如我们定义一个变量为 obj 的对象,然后我们把他赋值给另一个变量,这个过程就会涉及到浅拷贝问题,另一个变量只是之前变量的一份拷贝,前后两个变量的存储地址是公用的。你可以这么认为,赋值实际上是将地址绑定在一起

    这里有两个注意点:

    • 引用数据类型:对象,数组,函数等,这些拷贝的是地址
    let obj = {name: {code: '人生代码'}}
    let copyObj = obj

    console.log(copyObj.name === obj.name) // true 说明使用同一个引用地址
    • 基本数据类型:数字,字符串,布尔值这些是拷贝值
    let str = "Ken"
    let str1 = str

    console.log(str === str1) // true 但是只是拷贝了值

    深拷贝

    你可这么理解,就比如我们将衣服放在一个衣柜里面,后面你又买了一件一模一样的衣服,顺便也买了另一个衣柜给这件新衣服放起来,这样这两件衣服不就完全不相干了?

    深拷贝其实就是这个意思:

    将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

    let obj = {name: {code: '人生代码'}}

    let copyObj = JSON.parse(JSON.stringify(obj))

    console.log(copyObj.name === obj.name) // false 说明引用地址不一样了

    赋值和深/浅拷贝的区别

    我们这里比较的是关于引用类型:

    赋值

    赋值其实赋的是该对象,数组的引用地址,也就是说两个对象是联动的,存在关系的,只要其中一个变量改变,另外一个变量就会跟着改变。

    let obj = {
      name: '人生代码',
      arr: [1,2,3]
    }

    let obj1 = obj

    obj1.name = "Ken"
    obj1.arr = [4,5,6]

    console.log(obj1, obj) // obj1:{name: 'Ken', arr: [4,5,6]} obj:{name: 'Ken', arr: [4,5,6]}

    浅拷贝

    重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

    // 浅拷贝
    let obj1 = {
        name : '人生代码',
        arr : [1,2,3],
    };
    let obj2=shallowClone(obj1)
    obj2.name = "Ken";
    obj2.arr = [5,6,7] ; // 新旧对象还是共享同一块内存
    // 这是个浅拷贝的方法
    function shallowClone(source) {
        var target = {};
        for(var i in source) {
            if (source.hasOwnProperty(i)) {
                target[i] = source[i];
            }
        }
        return target;
    }
    console.log('obj1',obj1) 
    // obj1 { name: '人生代码', arr: [ 5,6,7 ] }
    console.log('obj2',obj2) 
    // obj2 { name: 'Ken', arr: [ 5, 6, 7 ] }

    深拷贝

    从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。

    // 深拷贝
    let obj1 = {
        name : '人生代码',
        arr : [1,2,3],
    };
    let obj2=deepClone(obj1)
    obj2.name = "Ken";
    obj2.arr = [5,6,7] ; // 新对象跟原对象不共享内存
    // 这是个深拷贝的方法
    function deepClone(obj) {
        if (obj === null) return obj; 
        if (obj instanceof Date) return new Date(obj);
        if (obj instanceof RegExp) return new RegExp(obj);
        if (typeof obj !== "object") return obj;
        let cloneObj = new obj.constructor();
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            // 实现一个递归拷贝
            cloneObj[key] = deepClone(obj[key]);
          }
        }
        return cloneObj;
    }
    console.log('obj1',obj1) 
    // obj1 { name: '人生代码', arr: [ 1, 2, 3 ] }
    console.log('obj2',obj2) 
    // obj2 { name: 'Ken', arr: [ 5, 6, 7 ] }
    面试官:老是问我深浅拷贝问题,自己看文章

    浅拷贝实现的方式

    Object.assign()

    let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
    let obj2 = Object.assign({}, obj1);
    obj2.person.name = "wade";
    obj2.sports = 'football'
    console.log(obj1); 
    // { person: { name: 'wade', age: 41 }, sports: 'basketball' }

    函数库lodash的_.clone方法

    var _ = require('lodash');
    var obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    var obj2 = _.clone(obj1);
    console.log(obj1.b.f === obj2.b.f);
    // true

    展开运算符...

    let obj1 = { name: 'Kobe', address:{x:100,y:100}}
    let obj2= {... obj1}
    obj1.address.x = 200;
    obj1.name = 'wade'
    console.log('obj2',obj2) 
    // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }

    Array.prototype.concat()

    let arr = [1, 3, {
        username: 'kobe'
    }];
    let arr2 = arr.concat();    
    arr2[2].username = 'wade';
    console.log(arr); 
    //[ 1, 3, { username: 'wade' } ]

    Array.prototype.slice()

    let arr = [1, 3, {
        username: ' kobe'
        }];
    let arr3 = arr.slice();
    arr3[2].username = 'wade'
    console.log(arr); 
    // [ 1, 3, { username: 'wade' } ]

    深拷贝的实现方式

    JSON.parse(JSON.stringify())

    函数库lodash的_.cloneDeep方法

    var _ = require('lodash');
    var obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    var obj2 = _.cloneDeep(obj1);
    console.log(obj1.b.f === obj2.b.f);
    // false

    jQuery.extend()方法

    var $ = require('jquery');
    var obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    var obj2 = $.extend(true, {}, obj1);
    console.log(obj1.b.f === obj2.b.f); 
    // false

    手写递归方法

    递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

    有种特殊情况需注意就是对象存在循环引用的情况,即对象的属性直接的引用了自身的情况,解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。关于这块如有疑惑,请仔细阅读ConardLi大佬如何写出一个惊艳面试官的深拷贝?这篇文章。

    function deepClone(obj, hash = new WeakMap()) {
      if (obj === null) return obj; 
      // 如果是null或者undefined我就不进行拷贝操作
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
      if (typeof obj !== "object") return obj;
      // 是对象的话就要进行深拷贝
      if (hash.get(obj)) return hash.get(obj);
      let cloneObj = new obj.constructor();
      // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
      hash.set(obj, cloneObj);
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 实现一个递归拷贝
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }
    let obj = { name: 1, address: { x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let d = deepClone(obj);
    obj.address.x = 200;
    console.log(d);

    参考文献

    一篇文章搞定 JavaScript 深浅拷贝

    起源地下载网 » 面试官:老是问我深浅拷贝问题,自己看文章

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元