js中的浅拷贝和深拷贝

时间:2021-04-01 19:54:26

一、浅拷贝和深拷贝

(1)基本类型

  5种基本数据类型,Number、String、Boolean、Null以及Undefined,变量是直接按值存放的,存放在栈内存中的简单数据,可以直接访问。

(2)引用类型

  存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。

  当需要访问引用类型(比如对象,数组)时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

浅拷贝

  如下面代码所示:

var x = {
    a:1,
    b:2
};
var y = x;
y.a = 3;
console.log(x);//{a: 3, b: 2}
console.log(y);//{a: 3, b: 2}

  这是浅拷贝,x和y指向的是同一个堆,对象复制只是复制的对象的引用

  复制的副本其实只是一个指针,两个变量指向同一个堆对象,改变其中一个变量,另一个也会受影响

深拷贝

  如下面代码所示:

var x = {
    a:1,
    b:2
};
var y = {
    a:x.a,
    b:x.b
};
y.a = 3;
console.log(x);//{a: 1, b: 2}
console.log(y);//{a: 3, b: 2}

  x对象和y对象是虽然所有的值都是一样的,但是在堆里面,对应的不是同一个了。

深拷贝和浅拷贝的示意图如下:

js中的浅拷贝和深拷贝

  浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。

   但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

 二、浅拷贝的实现方式

1.通过赋值实现

  将浅拷贝封装成一个函数,如下代码所示:

function shallowCopy (src) {
    var result = {};
    for(let prop in src){
        result[prop] = src[prop];
    }
    return result;
}
var obj = {
    a:1,
    arr:[2,3]
};
var shallowObj = shallowCopy(obj);
shallowObj.arr[1] = 5;
console.log(obj);
console.log(shallowObj);

  结果如下图所示:

js中的浅拷贝和深拷贝

2.通过Object.assign()实现

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

  但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

  如下面代码所示:

var x = {
    a:{
        b:1,
        c:2
    }
};
var y = Object.assign({},x);
y.a.b = 3;
console.log(x);//{a:{b:3,c:2}}
console.log(y);//{a:{b:3,c:2}}

  注意:当object只有一层的时候,是深拷贝,如下代码所示:

var x = {
    a:1,
    b:2
};
var y = Object.assign({},x);
y.a = 3;
console.log(x);//{a: 1, b: 2}
console.log(y);//{a: 3, b: 2}

三、深拷贝的实现方式

1.当对象只有一层时,通过Object.assign()实现,如上

2.JSON对象的stringify以及parse方法

  JSON.stringify方法将js对象序列化成JSON字符串,JSON.parse方法将JSON字符串反序列化为js对象。

  借助这两个方法,可以实现对象的深拷贝。

var x = {
    a:1,
    b:2
};
var y = JSON.parse(JSON.stringify(x));
y.a = 3;
console.log(x);//{a: 1, b: 2}
console.log(y);//{a: 3, b: 2}

3.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