最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JavaScript基础 - 深拷贝与浅拷贝的区别与实现

    正文概述 掘金(YanniLi)   2020-11-26   442

    (码字不易,转载请注明出处,谢谢。)

    可先了解《JavaScript基础 - 数据类型》

    变量的存储

    基本类型存储

    • 变量名和值都直接存储在栈内存中
    • 值与值之间独立存在,修改一个变量不会影响其他变量
    var a = 20;
    var b = a;
    a++;
    console.log(a, b) // 21 20
    

    JavaScript基础 - 深拷贝与浅拷贝的区别与实现

    引用类型存储

    • 变量名保存在栈中,对应的值是对象的内存地址(指针)
    • 堆内存中才保存了真正的值

    当改变一个变量的值时,另一个变量会跟着改变

    var obj1 = new Object();
    obj1.name = "mike";
    var obj2 = obj1;
    obj2.name = "jack";
    console.log(obj1.name); //jack
    

    JavaScript基础 - 深拷贝与浅拷贝的区别与实现

    清空一个变量的值时,只是断开该变量与对象的联系,另一个对象并不受影响

    var obj1 = new Object();
    obj1.name = "mike";
    var obj2 = obj1;
    obj2 = null;
    console.log(obj1); // {name: "mike"}
    console.log(obj2); // null
    

    JavaScript基础 - 深拷贝与浅拷贝的区别与实现

    • 堆比栈大,栈比堆速度快;
    • 基本数据类型比较稳定,而且相对来说占用的内存小;
    • 引用数据类型大小是动态的,而且是无限的,引用值的大小会改变,如果存放在栈中,会降低变量查找的速度,因此放在栈空间的值是该对象存储在堆中的地址,通过地址再去堆中查找真正的值;
    • 堆内存是无序存储,可以根据引用直接获取;

    数据比较

    • 当比较两个基本数据类型的值时,就是比较值;
    • 当比较两个引用数据类型时,比较的是对象的内存地址;
    var a = 10;
    var b = 10;
    console.log(a == b); // true => 值相等,为true
    var obj1 = new Object();
    var obj2 = new Object();
    var obj3 = obj1;
    console.log(obj1 == obj2);// false => 两个对象,引用地址不同
    console.log(obj1 == obj3);// true => 引用地址相同
    

    浅拷贝

    从上面的介绍中可以知道,当改变一个引用类型变量的值时候,另一个变量值也会跟着改变。这是因为使用 = 赋值的时候,属于浅拷贝。

    浅拷贝: 只拷贝对象的第一层属性,也就是只拷贝了其引用(指针),拷贝对象的改变会反应到原对象上。

    = 赋值

    var obj1 = {
        a: 1,
        b: 2,
        c: {
            c1: 3,
            c2: 4
        }
    };
    var obj2 = obj1;
    obj2.a = 100;
    obj2.c.c1 = 300;
    console.log(obj1.a, obj1.c.c1); // 100 300
    console.log(obj2.a, obj2.c.c1);// 100 300
    

    Object.assign()

    Object.assign() 可以实现第一层基本类型属性的深拷贝,其他更深层属于浅拷贝。

    var obj1 = {
        a: 1,
        b: 2,
        c: {
            c1: 3,
            c2: 4
        }
    };
    var obj2 = Object.assign({},obj1); // 需传{},否则第一层也属于浅拷贝
    obj2.a = 100;
    obj2.c.c1 = 300;
    console.log(obj1.a, obj1.c.c1); // 1 300
    console.log(obj2.a, obj2.c.c1); // 100 300
    
    

    手动实现浅拷贝

    只循环第一层,实现第一层的深拷贝

    function simpleCopy(obj1) {
       var obj2 = Array.isArray(obj1) ? [] : {};
       for (let i in obj1) {
       obj2[i] = obj1[i];
      }
       return obj2;
    }
    var obj1 = {
        a: 1,
        b: 2,
        c: {
            c1: 3,
            c2: 4
        }
    };
    var obj2 = simpleCopy(obj1);
    obj2.a[0] = 100;
    obj2.c.c1 = 300;
    console.log(obj1.a, obj1.c.c1); // 1 300
    console.log(obj2.a, obj2.c.c1); // 100 300
    

    深拷贝

    深拷贝:拷贝多层,每一级别的数据都会拷贝出来,拷贝对象的改变不会影响到原对象。

    使用 JSON 对象方法

    缺点:

    • 会抛弃对象的 constructor,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成 Object;
    • 只有可以转成 JSON 格式的对象才可以这样用,像 function 就没办法转成 JSON;
    var obj1 = {
        a: 1,
        b: ['a', 'b', 'c'],
        c: {
            c1: {
                x: 1
            }
        }
    };
    var obj2 = JSON.parse(JSON.stringify(obj1))
    obj2.c.c1.x = 300;
    console.log(obj1.c.c1.x); // 1
    console.log(obj2.c.c1.x); // 300
    
    // function 无法转换
    var obj3 = {
        fun: function() {
            alert(333)
        }
    }
    var obj4 = JSON.parse(JSON.stringify(obj3))
    console.log(typeof obj3.fun); // function
    console.log(typeof obj4.fun); // undefined
    
    

    Object.assign()

    • 当基本数据类型时,比如String,Number,Boolean时,属于深拷贝
    • 当引用数据类型时,比如Object,Array时,属于浅拷贝
    var obj1 = {
        a: 1,
        b: 2
    };
    var obj2 = Object.assign({},obj1); // 需要赋值给一个 {}
    obj2.a = 100;
    console.log(obj1.a) // 1
    console.log(obj2.a) // 100
    

    使用 Reflect

    Reflect是ES6为操作对象而提供的新API。 Reflect介绍

    function deepClone(obj) {
        if (!isObject(obj)) {
            throw new Error('obj 不是一个对象!')
        }
    
        let isArray = Array.isArray(obj)
        let cloneObj = isArray ? [...obj] : { ...obj }
        Reflect.ownKeys(cloneObj).forEach(key => {
            cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
        })
    
        return cloneObj
    }
    

    Object.create()

    创建一个新对象

    function deepClone(initalObj, finalObj) {    
      var obj = finalObj || {};    
      for (var i in initalObj) {        
        var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
        if(prop === obj) {            
          continue;
        }        
        if (typeof prop === 'object') {
          obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
        } else {
          obj[i] = prop;
        }
      }    
      return obj;
    }
    

    使用扩展运算符

    • 当基本数据类型时,比如String,Number,Boolean时,属于深拷贝
    • 当引用数据类型时,比如Object,Array时,属于浅拷贝
    
    var car = {brand: "BMW", price: "380000", length: "5米"}
    var car1 = { ...car, price: "500000" }
    console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
    console.log(car); // { brand: "BMW", price: "380000", length: "5米" }
    

    slice实现对数组的深拷贝

    • 当基本数据类型时,比如String,Number,Boolean时,属于深拷贝
    • 当引用数据类型时,比如Object,Array时,属于浅拷贝
    var arr1 = ["1","2","3"]; 
    var arr2 = arr1.slice(0);
    arr2[1] = "9";
    console.log("数组的原始值:" + arr1 );
    console.log("数组的新值:" + arr2 );
    

    concat实现对数组的深拷贝

    • 当基本数据类型时,比如String,Number,Boolean时,属于深拷贝
    • 当引用数据类型时,比如Object,Array时,属于浅拷贝
    // 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
    var arr1 = ["1","2","3"];
    var arr2 = arr1.concat();
    arr2[1] = "9";
    console.log("数组的原始值:" + arr1 );
    console.log("数组的新值:" + arr2 );
    
    // 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝
    var arr1 = [{a:1},{b:2},{c:3}];
    var arr2 = arr1.concat();
    arr2[0].a = "9";
    console.log("数组的原始值:" + arr1[0].a ); // 数组的原始值:9
    console.log("数组的新值:" + arr2[0].a ); // 数组的新值:9
    

    手动深拷贝

    lodash.js

    使用工具库 lodash

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

    $.extend

    jquery 提供一个$.extend 方法实现深拷贝;

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

    参考文章:

    • JS中变量存储
    • JS浅拷贝和深拷贝
    • js浅拷贝与深拷贝的区别和实现方式

    起源地下载网 » JavaScript基础 - 深拷贝与浅拷贝的区别与实现

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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