以下是浅拷贝与深拷贝的各种方法及其区别:
所需知识点:
typeof [],null,object 皆为object hasOwnProperty:过滤原型属性,只能获取自有属性
对象遍历:
-
- for in可以遍历可枚举属性,包括原型链上的属性;
-
- Object.keys返回一个由对象可枚举属性组成的数组,不包括原型属性;
-
- Object.getOwnProperty 返回对象的自有属性数组,包括可枚举的和不可枚举的,不包括原型属性
数组遍历:
-
- for in 输出数组的index,即'0','1','2','3','4','5'....
-
- Object.keys 同上
-
- Object.getOwnProperty 输出'0','1','2','3'...,'length'
测试数据:
const target = {
a: '1',
e: [1, 2, 3, 4, [5, 6, [7, 8, [9]]]],
b: {
c: [{
h: '1',
i: '2'
}],
d: {
f: {
g: '4'
}
}
},
j: null,
k: undefined,
l: function () {
console.log('l');
},
date: new Date(),
reg: /[A-Z]/g,
}
浅克隆
const shallClone = (target) => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? [] : {}
for (prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop]
}
}
return cloneTarget
} else {
return target
}
}
console.log('shallClone', shallClone(target));
// target.b = {} //此层拷贝的是值
// target.b.c = {} //此层拷贝的是引用
// target.b.c.h = '7' //此层拷贝的是引用
运行结果:
再执行下面代码
target.b.c = {} //此层拷贝的是引用
运行结果:
经测试,shallClone方法除函数拷贝后是个空函数外基本上可以成功拷贝,但是对象值的对象值只拷贝了引用,而不是值,即对象只拷贝了一层。
深克隆
1、第一种方法
function deepClone(target) {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? [] : {}
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop])
}
}
return cloneTarget
} else {
return target
}
}
console.log('deepClone', deepClone(target));
// target.b = {} //此层拷贝的是值
// target.b.c = {} //此层拷贝的是值
// target.b.c.h = '7' //此层拷贝的是值
运行结果:
经测试,deepClone方法可以拷贝整个对象的值,而不是引用,但是date格式和正则表达式拷贝失败,结果为空对象,函数拷贝失败,为空函数。
2、第二种方法
const isObject = (target) => (typeof target === 'object') && target !== null;
function deepClone2(target, map = new Map()) {
Date.prototype.clone = function () {
return new Date(this.valueOf());
}
RegExp.prototype.clone = function () {
var pattern = this.valueOf();
var flags = '';
flags += pattern.global ? 'g' : '';
flags += pattern.ignoreCase ? 'i' : '';
flags += pattern.multiline ? 'm' : '';
return new RegExp(pattern.source, flags);
};
// 先判断该引用类型是否被拷贝过
if (map.get(target)) {
return target;
}
// 检测当前对象target是否与 正则、日期格式对象匹配
if (eval(target) instanceof RegExp || target instanceof Date) {
console.log(target.clone())
return target.clone()
}
if (isObject(target)) {
map.set(target, true); // 为循环引用的对象做标记
const cloneTarget = Array.isArray(target) ? [] : {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone2(target[prop], map);
}
}
return cloneTarget;
} else {
return target;
}
}
console.log('deepClone2', deepClone2(target));
// target.b = {} //此层拷贝的是值
// target.b.c = {} //此层拷贝的是值
// target.b.c.h = '7' //此层拷贝的是值
运行结果:
经测试,除函数外都能拷贝成功。
3、第三种方法
function deepClone3(target) {
return JSON.parse(JSON.stringify(target))
}
console.log('deepClone3', deepClone3(target));
// target.b = {} //此层拷贝的是值
// target.b.c = {} //此层拷贝的是值
// target.b.c.h = '7' //此层拷贝的是值
运行结果:
经测试,除开undefined值和正则,函数不能拷贝,其他情况都能拷贝。