该文转载自http://www.cnblogs.com/zichi/p/4568150.html,有部分修改。
在聊JavaScript(以下简称js)深度克隆之前,我们先来了解一下js中对象的组成。
在 js
中一切实例皆是对象,具体分为 原始类型 和 合成类型 :
原始类型 对象指的是 Undefined
、 Null
、Boolean
、Number
和 String
,按值传递。
合成类型 对象指的是 array
、 object
以及 function
,按址传递,传递的时候是内存中的地址。
克隆或者拷贝分为2种: 浅度克隆 、 深度克隆 。
浅度克隆 :基本类型为值传递,对象仍为引用传递。
深度克隆 :所有元素或属性均完全克隆,并于原引用类型完全独立,即,在后面修改对象的属性的时候,原对象不会被修改。
又或许你刚听说“深度克隆”这个词,简单来说,就是说有个变量a,a的值是个对象(包括基本数据类型),现在你要创建一个变量b,使得它拥有跟a一样的方法和属性等等。但是a和b之间不能相互影响,即a的值的改变不影响b值的变化。直接赋值可好?
var a = 1;
var b = a;
a = 10;
console.log(b); // 1
var a = 'hello';
var b = a;
a = 'world';
console.log(b); // hello
var a = true;
var b = a;
a = false;
console.log(b); // true
实践证明某些 JavaScript
的原始数据类型,如果要克隆直接赋值即可。
关于 function
的深度复制:查阅了一些资料, function
的深度复制似乎和原始数据类型的深度复制一样。
var a = function () {
console.log(1);
};
var b = a;
a = function () {
console.log(2);
};
b(); // 1
本来我也是这么认为的,直到文章下出现了评论。思考后我觉得 function
和普通的对象一样,只是我们在平常应用中习惯了整体的重新赋值,导致它在深度复制中的表现和原始类型一致:
var a = function () {
console.log(1);
};
a.tmp = 10;
var b = a;
a.tmp = 20;
console.log(b.tmp); // 20
于是乎对于 function
类型的深度克隆,直接赋值似乎并不应该是一种最好的方法(尽管实际应用中足矣)。
但是对象呢?
var a = [0,1,2,3];
var b = a;
a.push(4);
console.log(b); // [0, 1, 2, 3, 4]
显然与预期不符,为什么会这样?因为原始数据类型储存的是对象的实际数据,而对象类型存储的是对象的引用地址。上面的例子呢也就是说a和b对象引用了同一个地址,无论改变a还是改变b,其实根本操作是一样的,都是对那块空间地址中的值的改变。
于是我们知道了,对于基本的对象来说,不能只能用 “ = ” 赋值,思索后写下如下代码:
// 判断arr是否为一个数组,返回一个bool值
function isArray (arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
// 深度克隆
function deepClone (obj) {
if(typeof obj !== "object" && typeof obj !== 'function') {
return obj; //原始类型直接返回
}
var o = isArray(obj) ? [] : {};
for(i in obj) {
if(obj.hasOwnProperty(i)){
o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
}
}
return o;
}
注意代码中判断数组的时候用的不是 obj instanceof Array
,这是因为该方法存在一些小问题,详情见 http://www.nowamagic.net/librarys/veda/detail/1250
用一些代码来测试下:
// 测试用例:
var srcObj = {
a: 1,
b: {
b1: ["hello", "hi"],
b2: "JavaScript"
}
};
var abObj = srcObj;
var tarObj = cloneObject(srcObj);
srcObj.a = 2;
srcObj.b.b1[0] = "Hello";
console.log(abObj.a);
console.log(abObj.b.b1[0]);
console.log(tarObj.a); // 1
console.log(tarObj.b.b1[0]); // "hello"
似乎可以解决一般的对象(包括 Array
)的深度克隆了,或许这儿会有疑问,new String(..)
这类的也是对象啊,可是这样写你克隆不了啊…但是楼主觉得深度克隆的考点不在这里,可能在于:
原始数据类型的直接赋值
function的exception
对象的深度克隆中Array类型的判断
克隆函数的递归调用
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。