TABLE OF CONTENTS
JS 对象的拷贝,有两种方式,一种是深拷贝(deep copy),一种是浅拷贝(shallow copy)。其实我不是很喜欢这种说法,我更喜欢对象克隆(clone)和复制引用(reference copy)的说法。
复制引用
如其名曰,复制引用只是复制对这个对象的引用,例如:
var obj = { a: 1,};
var newObj = obj;
这个时候,obj 和 newObj 实质上都保存着对同一个对象的引用,有点像 C 当中的指针的概念,obj 和 newObj 保存着同一个对象的地址,因此任何对 newObj 的操作,都会反映在 obj 上:
newObj.a = 2;console.log(obj.a); // 2
不过对于包装类型:Number, String, Boolean 来说,上面这种做法是无效的,因为他们只会拷贝值,并不会拷贝对其的引用,试想,如果连这些原始类型都能拷贝引用了,那么代码就根本没法维护了,时时刻刻都有可能误操作到别的对象上了。
对象克隆
对象克隆本质上就是创建一个新的空对象,然后遍历源对象(要克隆的那个对象)的属性,然后把源对象的属性都一一复制到新的对象上,这样就完成了对源对象的克隆。
我们还需要检测源对象的属性,如果它的属性也是一个对象,我们就要利用递归来进行深层次的拷贝,如果是原始类型, 直接复制到新对象上即可。
同时,还要防止循环引用,也就是 a.b = a 的情况,这种情况会引起死循环,所以我们也要对其进行检测。
function clone(obj) { var name; var copy = {};
// 如果穿进来的参数不是一个对象或者函数 if (typeof obj !== 'object' && typeof obj !== 'function') { return {}; }
// 如果对象的属性是数组 if (obj instanceof Array) { return Array.prototype.slice.call(obj); }
for (name in obj) { // 防止死循环 if (obj[name] === obj) { continue; } if (obj[name] && typeof obj[name] === 'object') { copy[name] = clone(obj[name]); } else if (obj[name]) { copy[name] = obj[name]; } } return copy;}
这样我们就完成了比较基本的对象克隆:
var bar = { a: { inner: 123, inside: { test: 'you can see me', }, }, b: [1, 2, 3], c: 'string', d: function(a, b) { return a + b; },};
var foo = clone(bar);console.log(foo);
输出:
{ a: { inner: 123, inside: { test: 'you can see me' } }, b: [ 1, 2, 3 ], c: 'string', d: [Function: d] }