因为前几天在写项目的时候要用到深拷贝,所以就去查了一些相关的数据,趁这个机会做点笔记。 基本类型(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
在修改a 时并不会改到b
但对象就不同,对象传的是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 的方式。
自己手动复制像上面的范例,把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 时也会修改到旧的。
Object.assignObject.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。不过如果要复制的对象只有一层的话可以考虑使用它。
转成 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 会直接消失,所以这个方法只能用在单纯只有数据的对象。 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
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
这是我比较推荐的方法,性能还不错,使用起来也很简单。
|