关于JavaScript的浅拷贝和深拷贝

一大早上被领导问了几个问题,然后一脸懵B的去查了一下资料,最后做一下整理

一、先来了解一下:基本型别(Primitive Type) VS 物件(Object)


在 JS 中有一些基本型别像是Number、String、Boolean
而物件就是像这样的东西{ name: 'Larry', skill: 'Node.js' }

物件跟基本型别最大的不同就在于他们的传值方式

基本型别是传 value,像是这样

var a = 10;
var b = a;
b = 20;

console.log(a); // 10
console.log(b); // 20


在修改b时并不会改到a

但物件就不同,物件传的是 reference

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = obj1;
obj2.b = 100;

console.log(obj1); // { a: 10, b: 100, c: 30 } <-- b 被改到了
console.log(obj2); // { a: 10, b: 100, c: 30 }


复制一份obj1叫做obj2
然后把obj2.b改成100
但却不小心改到obj1.b
因为他们根本是同一个物件
这就是所谓的浅拷贝

要避免这样的错误发生就要写成这样

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;

console.log(obj1); // { a: 10, b: 20, c: 30 } <-- b 没被改到
console.log(obj2); // { a: 10, b: 100, c: 30 }


这样就是深拷贝
不会改到原本的obj1

二、浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)


浅拷贝只复制指向某个物件的指标
而不复制物件本身
新旧物件还是共用同一块记忆体

但深拷贝会另外创造一个一模一样的物件
新物件跟原物件不共用记忆体
修改新物件不会改到原物件

如何做到 Deep Copy
要完全复制又不能修改到原物件
这时候就要用 Deep Copy
这裡会介绍几种 Deep Copy 的方式

1.自己手动复制
像上面的范例
把obj1的属性一个个复制到obj2中

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;

console.log(obj1); // { a: 10, b: 20, c: 30 } <-- 没被改到
console.log(obj2); // { a: 10, b: 100, c: 30 }


但这样很麻烦要自己慢慢复制
而且这样其实不是 Deep Copy
如果像下面这个状况

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时也会修改到旧的

2.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跟我们手动复制的效果相同
所以一样只能处理深度只有一层的物件
没办法做到真正的 Deep Copy
不过如果要复制的物件只有一层的话可以考虑使用他

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
相信大家应该都很熟悉 jquery 这个 library
jquery 有提供一个$.extend可以用来做 Deep Copy

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
另外一个很热门的函式库 lodash
也有提供_.cloneDeep用来做 Deep Copy

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://larry850806.github.io/2016/09/20/shallow-vs-deep-copy/

发布了29 篇原创文章 · 获赞 4 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/cccdf_jjj/article/details/95587848