对象克隆-浅拷贝与深拷贝
对象的克隆(clone)也叫拷贝、复制。
我们都知道,对象在赋值的过程中其实是复制了该对象的引用地址,所以一方面改变,另一方也会跟着变。
js
var person1 = {
name: '张三',
age: 18
}
var person2 = person1;
person2.name = '李四';
console.log(person1); // {name: "李四", age: 18}
console.log(person2); // {name: "李四", age: 18}
那么,我们如何去克隆一个对象呢。
浅拷贝
浅拷贝的实现方式还是挺多的,这里列举两种方式:
-
使用
Object.assign()
:jsvar person1 = { name: '张三', age: 18 } var person2 = Object.assign({}, person1); person2.name = '李四'; console.log(person1); // {name: "张三", age: 18} console.log(person2); // {name: "李四", age: 18}
-
遍历
jsfunction clone (origin, target) { var tar = target || {}; for (var key in origin) { if (origin.hasOwnProperty(key)) { // 只复制对象自身的属性 tar[key] = origin[key]; } } return tar; } var person1 = { name: '张三', age: 18 } var person2 = clone(person1); person2.name = '李四'; // 浅拷贝无法解决引用值的问题 person2.son.push('张小三'); console.log(person1); // {name: "张三", age: 18, son: ["张小一", "张小二", "张小三"]} console.log(person2); // {name: "李四", age: 18, son: ["张小一", "张小二", "张小三"]}
深拷贝
为了解决对象内的引用值的问题,我们需要使用深拷贝。
js
function deepClone (origin, target) {
var tar = target || {},
toStr = Object.prototype.toString,
arrType = '[object Array]',
item;
for (var key in origin) {
if (origin.hasOwnProperty(key)) {
item = origin[key];
if (typeof item === 'object' && item !== 'null') { // 引用值,并且不是null的情况
if (toStr.call(item) === arrType) { // 值是一个array
tar[key] = [];
} else {
tar[key] = {};
}
// 递归克隆
deepClone(item, tar[key]);
} else {
tar[key] = origin[key];
}
}
}
return tar;
}
var person1 = {
name: '张三',
son: {
first: {
name: '张小一',
car: ['Benz']
},
second: {
name: '张小二',
car: []
}
},
car: ['Mazda']
}
var person2 = deepClone(person1);
person2.son.third = {
name: '张小三'
}
person2.son.second.car.push('BMW');
console.log(person1.son);
// {
// first: {
// name: '张小一',
// car: ['Benz']
// },
// second: {
// name: '张小二',
// car: []
// }
// }
console.log(person2.son);
// {
// first: {
// name: '张小一',
// car: ['Benz']
// },
// second: {
// name: '张小二',
// car: ['BMW']
// },
// third: {
// name: '张小三'
// }
// }
深拷贝的另一种方式
使用JSON
的方法也可以快速的实现对象的深拷贝。
js
var person1 = {
son: {
name: '张三',
car: ['Benz']
}
}
var str = JSON.stringify(person1), // 先将对象转换成字符串的形式
person2 = JSON.parse(str); // 然后再将JSON字符串转换成对象
person2.son.name = '李四';
person2.son.car.push('BMW');
console.log(person1);
// {
// son: {
// name: '张三',
// car: ['Benz']
// }
// }
console.log(person2);
// {
// son: {
// name: '李四',
// car: ['Benz', 'BMW']
// }
// }
虽然使用JSON
的方式很方便,但是这种方式却存在着不少的问题:
-
会丢失
function
、undefined
; -
对于
RegExp
、Error
对象,只会得到空对象; -
对于
date对象
,只会得到一个String
; -
对于
NaN
、Infinity
,会变成null;jsvar test = { a: function () {}, b: undefined, c: /abc/, d: new Error('err'), e: new Date(), f: NaN, g: Infinity, h: -Infinity } var test2 = JSON.parse(JSON.stringify(test)); console.log(test2); // c: {} // d: {} // e: "2020-02-19T08:34:46.962Z" // f: null // g: null // h: null // 当然,这里面对其它的引用类型并未做修改,所以得出的结果也不尽人意 var test3 = deepClone(test); console.log(test3); // a: ƒ () // b: undefined // c: {} // d: {} // e: {} // f: NaN // g: Infinity // h: -Infinity
-
无法处理循环引用
jsvar test = {}; test.obj = test; console.log(JSON.parse(JSON.stringify(test))); // Uncaught TypeError: Converting circular structure to JSON // 当然,没人这样写一个对象