一、克隆(拷贝)
1、浅拷贝
拷贝就是复制,就相当于把一个对象中的所有内容,复制一份给另一个对象(直接复制)。或者说就是把一个对象的地址给了另一个对象,他们指向相同,两个对象之间有共同的属性或者方法,都可以使用
var obj = {
name: "James",
age: 123,
card: ["visa", "master"],
wife: {
name: "Wade",
son: {
name: "Anthony",
}
}
}
var obj1 = {};
function clone(target, origin) {
for (var prop in origin) {
target[prop] = origin[prop];
}
}
clone(obj1,obj);
console.log(obj);
console.log(obj1);
打印结果如下:
延续上面代码,现在我们来改变拷贝后的obj1中的引用属性值:
obj1.card.push("银联");
obj1.wife.name = "Rose";
console.log(obj.card); // ["visa", "master", "银联"]
console.log(obj.wife.name); // Rose
console.log(obj1.card); // ["visa", "master", "银联"]
console.log(obj1.wife.name); // Rose
这里主要区分原始值和引用值的拷贝,原始值的拷贝,拷贝的是整个原始值,而引用值的拷贝,拷贝的是地址,如果是浅拷贝,一旦obj对象中含有引用值。拷贝之后,如果对obj1进行改变,则obj的值也会发生改变。
2、采用 instanceof 深拷贝
把一个对象中所有的属性或者方法,依次找到,并且在另一个对象中开辟相应的空间,依次的存储到另一个对象中,这句话可能不太好理解。简单点说,深拷贝之后,两个对象之间有共同的属性或者方法,但是它们各自指向各自空间(指向不同)。如果是深拷贝,一旦obj对象中含有引用值,拷贝之后,如果对obj1进行改变,则obj的值不会进行改变。
var obj = {
name: "James",
age: 123,
card: ["visa", "master"],
wife: {
name: "Wade",
son: {
name: "Anthony",
}
}
}
var obj1 = {};
// 通过函数实现,把对象origin中的所有的数据深拷贝到对象target中
function clone(target, origin) {
for (var prop in origin) {
var item = origin[prop]; // 先获取origin对象中每个属性的值
if (item instanceof Array) { // 判断这个属性的值是不是数组
target[prop] = []; // 如果是数组,那么在target对象中添加一个新的属性,并且这个属性值也是数组
clone(target[prop], item); // 调用这个方法,把origin对象中这个数组的属性的值依次的复制到target对象的这个数组的属性中
} else if (item instanceof Object) { // 判断这个属性的值是不是对象类型的
target[prop] = {}; // 如果是对象类型的,那么在target对象中添加一个新的属性,是一个空对象
clone(target[prop], item); // 再次调用这个函数,把origin对象中的属性对象的值依次的复制到target对象的这个属性对象中
} else {
target[prop] = origin[prop] // 如果值是普通的数据,直接复制到target对象的这个属性中
}
}
}
clone(obj1, obj);
console.log(obj);
console.log(obj1);
打印结果如下:
延续上面代码,现在我们再来改变拷贝后的obj1中的引用属性值:
obj1.card.push("银联");
obj1.wife.name = "Rose";
console.log(obj.card); // ["visa", "master"]
console.log(obj.wife.name); // Wade
console.log(obj1.card); // ["visa", "master", "银联"]
console.log(obj1.wife.name);// Rose
上面深拷贝我们采用了 instanceof 来进行判断是数组还是对象,也可以换一种思考方式如采用Object.prototype.toString的方式:
3、采用 Object.prototype.toString.call() 深拷贝
var obj = {
name: "James",
age: 123,
card: ["visa", "master"],
wife: {
name: "Wade",
son: {
name: "Anthony",
}
}
}
var obj1 = {};
// 思路:首先遍历对象for...in... 1、判断是不是原始值 2、判断是数组还是对象 3、建立相应的数组或对象
function deepClone(origin, target) {
var target = target || {},
toStr = Object.prototype.toString, // 让代码更简介;
arrStr = "[object Array]"; // 要进行比对的值
for (var prop in origin) {
if (origin.hasOwnProperty(prop)) { // 判断是否属于自身上的数据,避免非原型上的数据
if (origin[prop] !== "null" && typeof(origin[prop]) == "object") { // 判断如果原对象的属性值不等于null,并且类型为引用类型
if (toStr.call(origin[prop]) == arrStr) { // 判断这个属性的值是不是数组
target[prop] = []; // 如果是数组,那么在target对象中添加一个新的属性,并且这个属性值也是数组
} else {
target[prop] = {}; // 如果不是数组,那么在target对象中添加一个新的属性,是一个空对象
}
deepClone(origin[prop], target[prop]); // (递归)再次调用这个函数,把origin对象中的属性对象的值依次的复制到target对象的这个属性对象中
} else {
target[prop] = origin[prop]; // 如果值是普通的数据,直接复制到target对象的这个属性中
}
}
}
return target;
}
deepClone(obj, obj1);
console.log(obj);
console.log(obj1);
打印结果如下:
延续上面代码,现在我们再来验证改变拷贝后的obj1中的引用属性值:
obj1.card.push("银联");
obj1.wife.name = "Rose";
console.log(obj.card); // ["visa", "master"]
console.log(obj.wife.name); // Wade
console.log(obj1.card); // ["visa", "master", "银联"]
console.log(obj1.wife.name);// Rose
最后,优化下上面函数代码,让它看起来更加简洁:
function deepClone(origin, target) {
var target = target || {},
toStr = Object.prototype.toString, // 让代码更简介;
arrStr = "[object Array]"; // 要进行比对的值
for (var prop in origin) {
if (origin.hasOwnProperty(prop)) {
if (origin[prop] !== "null" && typeof(origin[prop]) == "object") {
target[prop] = (toStr.call(origin[prop]) == arrStr) ? [] : {}; // 采用三目运算符的方式优化代码
deepClone(origin[prop], target[prop]);
} else {
target[prop] = origin[prop];
}
}
}
return target;
}
deepClone(obj, obj1);
总结:深拷贝思路:
首先遍历对象for...in...
1、判断是不是原始值 typeof Object
2、判断是数组还是对象 instanceof/isPrototypeOf()/Object.prototype.toString.call()/constructor
3、建立相应的数组或对象
for...in...不仅可以遍历对象,也可以遍历数组,因为数组也是对象(数组的属性可以看作是前面的索引)
var arr = [1, 2, 3, 4, 5];
for (var prop in arr) {
console.log(prop + ":" + arr[prop]);
}
打印结果如下: