克隆概念
浅复制:原始类型为值传递,对象类型仍为引用传递。
深复制:所有元素或属性均完全复制,与原对象完全脱离,也就是说所有对于新对象的修改都不会反映到原对象中。
浅复制
浅复制就是简单的引用复制
var src = {
name:"src"
}
//复制一份src对象的应用
var target = src;
target.name = "target";
console.log(src.name); //输出target
target对象只是src对象的引用值的复制,因此target的改变也会影响src。
深复制
Array的slice和concat方法
Array的slice和concat方法都会返回一个新的数组实例,但是这两个方法对于数组中的对象元素却没有执行深复制,而只是复制了引用了,因此这两个方法并不是真正的深复制,通过以下代码进行理解:
var array = [1,2,3];
var array_shallow = array;
var array_concat = array.concat();
var array_slice = array.slice(0);
console.log(array === array_shallow); //true
console.log(array === array_slice); //false
console.log(array === array_concat); //false
可以看出,concat和slice返回的不同的数组实例,这与直接的引用复制是不同的。
var array = [1, [1,2,3], {name:"array"}];
var array_concat = array.concat();
var array_slice = array.slice(0);
//改变array_concat中数组元素的值
array_concat[1][0] = 5;
console.log(array[1]); //[5,2,3]
console.log(array_slice[1]); //[5,2,3]
//改变array_slice中对象元素的值
array_slice[2].name = "array_slice";
console.log(array[2].name); //array_slice
console.log(array_concat[2].name); //array_slice
通过代码的输出可以看出concat和slice并不是真正的深复制,数组中的对象元素(Object,Array等)只是复制了引用
JSON对象的parse和stringify
JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深复制。
var source = {
name:"source",
child:{
name:"child"
}
}
var target = JSON.parse(JSON.stringify(source));
//改变target的name属性
target.name = "target";
console.log(source.name); //source
console.log(target.name); //target
//改变target的child
target.child.name = "target child";
console.log(source.child.name); //child
console.log(target.child.name); //target child
从代码的输出可以看出,复制后的target与source是完全隔离的,二者不会相互影响。 这个方法使用较为简单,可以满足基本的深复制需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深复制(而且会直接丢失相应的值),同时如果对象中存在循环引用的情况也无法正确处理
jQuery中的extend复制方法
jQuery中的extend方法可以用来扩展对象,这个方法可以传入一个参数:deep(true or false),表示是否执行深复制(如果是深复制则会执行递归复制)
自己实现一个copy方法
//util作为判断变量具体类型的辅助模块
var util = (function () {
var class2type = {};
["Null", "Undefined", "Number", "Boolean", "String", "Object", "Function", "Array", "RegExp", "Date"].forEach(function (item) {
class2type["[object " + item + "]"] = item.toLowerCase();
});
function isType(obj, type) {
return getType(obj) === type;
}
function getType(obj) {
return class2type[Object.prototype.toString.call(obj)] || "object";
}
return {
isType: isType,
getType: getType
}
})();
function copy(obj, deep) {
//如果obj不是对象,那么直接返回值就可以了
if (obj === null || typeof obj !== "object") {
return obj;
}
//定义需要的局部变量,根据obj的类型来调整target的类型
var i, target = util.isType(obj, "array") ? [] : {},
value, valueType;
for (i in obj) {
value = obj[i];
valueType = util.getType(value);
//只有在明确执行深复制,并且当前的value是数组或对象的情况下才执行递归复制
if (deep && (valueType === "array" || valueType === "object")) {
target[i] = copy(value);
} else if (deep && valueType !== "array" && valueType !== "object") {
target[i] = eval(value.toString());
} else {
target[i] = value;
}
}
return target;
}