LouieLiu

关于JS的深浅拷贝

浅复制

  • 关于普通数组或者对象的复制最简单的方式就是直接赋值

    因为a和b指向同一个块内存 所以会修改b会影响a

    1
    2
    3
    4
    5
    6
    var a = [1,2,3,4];
    var b = a;
    console.log(b); // =>[1,2,3,4]
    b.push(5);
    console.log(a); // => [1,2,3,4,5]
    console.log(b); // => [1,2,3,4,5]
    1
    2
    3
    4
    5
    6
    var a = {key:1};
    var b = a;
    console.log(a===b) // => true
    b.key = 2;
    console.log(a.key) // => 2
  • 另一种直接的方法就是利用循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var a = [1,2,3,4];
    var b = [];
    for(let i in a){
    b[i] = a[i];
    }
    console.log(b); // => [1,2,3,4]
    b.push(5);
    console.log(a); // => [1,2,3,4]
    console.log(b); // => [1,2,3,4,5]
  • 利用slice属性和concat属性可以完成数组或对象的浅拷贝

    1
    2
    3
    4
    5
    6
    var a = [1,2,3,4];
    var b = a.slice();
    console.log(b); // => [1,2,3,4]
    b.push(5);
    console.log(a); // => [1,2,3,4]
    console.log(b); // => [1,2,3,4,5]
    1
    2
    3
    4
    5
    6
    7
    var a = ["key",{count:1}];
    var b = a.slice(); // 返回一个a的浅拷贝对象
    a==b // => false b是a的一个实例
    b[0] = "game";
    console.log(a[0]); // =>"key" a和b中的基本类型值 复制的为副本 不会相互影响
    b[1].count = 2;
    console.log(a[1].count) // => 2 a和b中的引用类型值 指向相同
  • 利用Object.assign()

    Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var x = {
    a:1,
    b:{
    f:{
    g:1
    }
    },
    c:[1,2,3]
    };
    var y = Object.assign({},x);
    console.log(y.b.f===x.b.f) // => true

  • 实现一个关于对象的浅复制

    在使用浅拷贝时如果改变子对象 并且属性恰好为一个对象 那么也自然会修改原对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function shallowCopy(p,c){
    var i;
    c = c||{};
    for(i in p){
    if(p.hasOwnProperty(i)){
    c[i] = p[i];
    }
    }
    return c;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var dad = {
    count:[1,2,3],
    game:{Inside:true}
    };
    var kid = shallowCopy(dad);
    kid.count.push(4);
    kid.count.toString(); // => "1,2,3,4"
    dad.count.toString(); // => "1,2,3,4"
    dad.game === kid.game; // => true

深复制

  • 关于对象的深度复制 可实现继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function deepCopy(p,c){
    var i;
    c = c||{};
    for(i in p){
    if(p.hasOwnProperty(i)){
    if(typeof p[i]==="object"){
    c[i] = Array.isArray(p[i])?[]:{};
    deepCopy(p][i],c[i]);
    }else{
    c[i] = p[i];
    }
    }
    }
    return c;
    }

    上述代码在复制时会进行类型检查 如果复制对象为一个数组或者对象会递归的遍历属性对象并将属性元素复制出来

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var dad = {
    count:[1,2,3],
    game:{Inside:true}
    };
    var kid = deepCopy(dad);
    kid.count.push(4);
    kid.count.toString(); // => "1,2,3,4"
    dad.count.toString(); // => "1,2,3"
    dad.game === kid.game; // => false
    kid.game.Inside = false;
    kid.game.Limbo = true;
    dad.game.Inside; // => true
  • JSON对象的parse和stringify 也可实现深度复制
    JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象 通过这两个方法 可以实现对象的深复制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var dad = {
    count:[1,2,3],
    game:{Inside:true}
    };
    var kid =JSON.parse(JSON.stringify(dad));
    kid.count.push(4);
    kid.count.toString(); // => "1,2,3,4"
    dad.count.toString(); // => "1,2,3"
    dad.game === kid.game; // => false
    kid.game.Inside = false;
    kid.game.Limbo = true;
    dad.game.Inside; // => true