在日常开发中,我们经常会遇到需要复制一个对象。但是如果只是简单的进行赋值,并不能实现对象的克隆(拷贝)。只是多了一份指向对象的应用而已。对象克隆分为浅克隆和深克隆。
浅克隆
对象的浅克隆只是会克隆对象的第一层属性。当被克隆对象的属性为一个对象或则数组时。浅克隆是不会对该属性进一步克隆而是会赋值改属性的引用。即当修改了其中一个对象的属性的值时另外的那个对象的对应值也会跟着发生变化。具体表现如下所示:
const obj = {
name: '薛清扬',
age: 24,
job: '前端开发',
hobbies: ['basketball', 'music', 'movies', 'cooking'],
dog: {
name: 'wangcai'
},
test: () => {
console.log('obj');
},
a: undefined
}
// 浅克隆
const shallowClone = (origin, tar = {}) => {
for (let key in origin) {
if (origin.hasOwnProperty(key)) { // 克隆对象只克隆对象本身的属性,忽略对象原型上的属性
tar[key] = origin[key];
}
}
return tar;
}
const newObj = shallowClone(obj);
obj.dog.name = 'xiaohua'; // 更改obj对象中dog中的name属性
console.log(newObj.dog.name); // xiaohua
深克隆
对象的深克隆就不会出现浅克隆的那种情况,它会递归(完全)的克隆整个对象,当对象的属性为一个对象或则数组时它会对改属性实现克隆而不是像浅克隆那样赋值引用。所以当一个对象被深克隆之后它是完全独立于被克隆对象的。即他们二者的不存在任何关联。具体示例如下所示:
const obj = {
name: '薛清扬',
age: 24,
job: '前端开发',
hobbies: ['basketball', 'music', 'movies', 'cooking'],
dog: {
name: 'wangcai'
},
test: () => {
console.log('obj');
},
a: undefined
}
// 深克隆
const deepClone = (origin, target = {}) => {
// 用对象的toString方法判断属性的类型
const toString = Object.prototype.toString;
const arrType = '[object Array]';
for (let key in origin) {
if (origin.hasOwnProperty(key)) { //只克隆对象本身上的属性
// 判断对象的属性是否为引用值:Object
if (typeof origin[key] === 'object' && origin[key] !== null) {
target[key] = toString.call(origin[key]) === arrType ? [] : {};
// 递归。继续克隆该属性指直到该属性的值不再是引用值
deepClone(origin[key], target[key]);
} else {
target[key] = origin[key];
}
}
}
return target;
}
const deepObj = deepClone(obj);
obj.dog.name = 'xiaohua';
console.log(obj.dog.name) // xiaohua
console.log(deepObj.dog.name); // wangcai
常见的深克隆方法
1、原生js通过递归实现深克隆。本文所示
2、使用JSON对象转化。但该方法有个缺陷,不能克隆对象中的方法,RegExp对象以及undefined类型的值。
3、借助其它库封装的方法实现深克隆。例如Jquery
总结
本文主要介绍了原生js实现对象深克隆和浅克隆,在实现深克隆时用到了递归的思想、以及通过借助Object原型上的toString方法判断一个属性是对象还是数组。