zoukankan      html  css  js  c++  java
  • js之深拷贝、浅拷贝

    浅拷贝

    对于基本类型,浅拷贝是对值的复制,对于对象来说,浅拷贝只复制指向某个对象的指针,而不复制对象本身,并没有开辟新的栈,也就是复制的结果是新旧对象还是共享同一块内存,两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变。

    深拷贝

    深拷贝会开辟新的栈,创造一个一模一样的对象,两个对象对应两个不同的地址,不共享内存,修改一个对象的属性,不会改变另一个对象的属性。

    基本类型 和 对象类型
    他们最大的区别就是在于他们的传值方式。 基本类型是传值 对象类型就是传引用。

    <script>
        var a = 100;
        var b = a;
        b = 200;
        console.log(a);   //100
        console.log(b);   //200
        //修改a的时候,b不会改变
       //基本类型是按值传递,像是这样:在修改a时并不会改到b
    
        var obj1 = {
            a:100,
            b:200,
            c:300
        }
        var obj2 = obj1;
        obj2.b = 3647367;  //修改b
        console.log(obj1);   //{a: 100, b: 3647367, c: 300}
        console.log(obj2);   //{a: 100, b: 3647367, c: 300}
        //但对象就不同,对象传的是按引用传值
        //因为他们根本是同一个对象,这就是所谓的浅拷贝。
    </script>
    

    这里复制一份obj叫做obj2, 这里修改了obj2的b为3647367同时也修改了obj1.b。 因为他们本来就是一个对象 这就是所谓的浅拷贝。
    避免这样的情况 我们这样写

    var obj1 = {
            a: 100,
            b: 200,
            c: 300
        }
        var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }
        obj2.b = 3647367;  //修改b
        console.log(obj1);   //{a: 100, b: 200, c: 300}
        console.log(obj2);   //{a: 100, b: 3647367, c: 300}
    

    这就是深拷贝 不会改到原来的obj1。

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

    要完全复制又不能修改到原对象,这时候就要用深拷贝,下面是深拷贝的几种方式:

    • 1、自己手动复制
      像上面的范例,把obj1的属性一个个复制到obj2中:
    var obj1 = {
            a: 100,
            b: 200,
            c: 300
        }
        var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }
        obj2.b = 3647367;  //修改b
        console.log(obj1);   //{a: 100, b: 200, c: 300}
        console.log(obj2);   //{a: 100, b: 3647367, c: 300}
    

    但这样很麻烦要自己慢慢复制,而且这样其实不是深拷贝,如果像下面这个状况。

    var obj1 = { body: { a: 10 } };
    var obj2 = { body: obj1.body }; 
    obj2.body.a = 20;
    console.log(obj1);     // { body: { a: 20 } } <-- 被改到了
    console.log(obj2);     // { body: { a: 20 } }
    console.log(obj1 === obj2);      // false
    console.log(obj1.body === obj2.body);    // true
    

    虽然obj1跟obj2是不同对象,但他们会共享同一个obj1.body,所以修改obj2.body.a时也会修改到旧的。

      1. Object.assign
        Object.assign是 ES6 的新函数,可以帮助我们达成跟上面一样的功能。
        var obj1 = { a: 10, b: 20, c: 30 };
        var obj2 = Object.assign({}, obj1);
        obj2.b = 100;
        console.log(obj1);    // { a: 10, b: 20, c: 30 } <-- 沒被改到
        console.log(obj2);   // { a: 10, b: 100, c: 30 }
    

    Object.assign({}, obj1)的意思是先建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj1。因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的深拷贝,不过如果要复制的对象只有一层的话可以考虑使用它。
    如下有嵌套对象时,则没有办法做到深拷贝:

        var obj1 = { a: 0 , b: { c: 0}};
        var obj2 = Object.assign({}, obj1);
        console.log(obj2); // { a: 0, b: { c: 0}}
    
        obj1.a = 1;
        console.log(obj1);  // { a: 1, b: { c: 0}}
        console.log(obj2);   // { a: 0, b: { c: 0}}
    
        obj2.a = 2;
        console.log(obj1);    // { a: 1, b: { c: 0}}
        console.log(obj2);    // { a: 2, b: { c: 0}}
    
        obj2.b.c = 3;
        console.log(obj1);    // { a: 1, b: { c: 3}}
        console.log(obj2);    // { a: 2, b: { c: 3}}
    
    • 3、转成 JSON 再转回来
      用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
    var obj1 = { body: { a: 10 } };
    var obj2 = JSON.parse(JSON.stringify(obj1)); 
    obj2.body.a = 20;
    console.log(obj1);    // { body: { a: 10 } } <-- 沒被改到
    console.log(obj2);    // { body: { a: 20 } }
    console.log(obj1 === obj2);     // false
    console.log(obj1.body === obj2.body);      // false
    

    这样做是真正的Deep Copy,但只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。

    var obj1 = { fun: function(){ console.log(123) } };
    var obj2 = JSON.parse(JSON.stringify(obj1));
    console.log(typeof obj1.fun);  // 'function'
    console.log(typeof obj2.fun);  // 'undefined' <-- 没复制
    

    要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。

    • 4、jquery的$.extend

    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
    

    5、lodash的_.cloneDeep
    函数库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
    

    这是比较推荐的方法,性能还不错,使用起来也很简单。

    参考:https://www.cnblogs.com/lmjZone/p/8521443.html
    https://www.cnblogs.com/syomm/p/5903740.html

  • 相关阅读:
    javascript/jquery操作cookie
    更改IE/FireFox查看源代码的默认编辑器,比如notepad++
    javascript refresh page 几种页面刷新的方法
    C# Enum,Int,String的互相转换 枚举转换
    js中两个感叹号的作用
    JQuery操作iframe
    JQuery判断一个元素下面是否有内容或者有某个标签
    Meta标签详解
    五一放假回校,真爽
    ASP.NET错误处理(一)摘自MSDN
  • 原文地址:https://www.cnblogs.com/jessie-xian/p/11596081.html
Copyright © 2011-2022 走看看