先讲一下浅克隆和深克隆的区别 浅克隆和深克隆的区分在于,引用类型的值是是复制了某个对象的指针,还是复制了对象本身
浅克隆方法:
- Object.assign() [第一层深拷贝,第二层以上都是浅拷贝]
- ...(扩展运算符)[Symbol.iterator]
深克隆
- JSON.parse 和 JSON.stringify
- lodash
- 手写一个
先自己手写一个克隆
function cloneDeep(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
const result = {};
Object.entries(obj).forEach(([key, value]) => {
result[key] = cloneDeep(value)
})
return result;
}
。。。我怎么一下就写出来了 仔细一看,我漏了两个地方 1.数组 2.无限循环
数组,判断一下,当前是数组还是对象 无限循环,增加一个hash记录之前的对象的地址,如果引用相同地址,就返回新的对象
function cloneDeep(obj, Hash = new WeakMap()) {
if (typeof obj !== 'object' || obj === null) return obj;
const result = Array.isArray(obj) ? [] : {};
if (Hash.has(obj)) return Hash.get(obj)
Object.entries(obj).forEach(([key, value]) => {
result[key] = cloneDeep(value,Hash)
})
Hash.set(obj, result);
return result;
}
测试一下
var a = {
x1: '1',
x2: 1,
x3: false,
x4: undefined,
x5: null,
x6: NaN,
x7: Symbol(),
x8: function() {},
x9: [],
x10: {},
x11: [[]],
x12: {x1:{}},
x13: {x1:a}
}
var b = cloneDeep(a);
// 先测试第一层
Object.entries(a).forEach(([key,value]) => {
console.log(key, b[key] === value)
})
x1 true
x2 true
x3 true
x4 true
x5 true
x6 false
x7 true
x8 true
x9 false
x10 false
x11 false
x12 false
x13 false
// 测试第二层
a.x11[0] === b.x11[0] // false
a.x12['x1'] === b.x12['x1'] // false
a === b.x13['x1'] // false
暂时测试通过。
看看别人怎么写的递归深克隆
function deepClone(origin, target, hash = new WeakMap()) {
//origin:要被拷贝的对象
// 需要完善,克隆的结果和之前保持相同的所属类
var target = target || {};
// 处理特殊情况
if (origin == null) return origin; //null 和 undefined 都不用处理
if (origin instanceof Date) return new Date(origin);
if (origin instanceof RegExp) return new RegExp(origin);
if (typeof origin !== 'object') return origin; // 普通常量直接返回
// 防止对象中的循环引用爆栈,把拷贝过的对象直接返还即可
if (hash.has(origin)) return hash.get(origin);
hash.set(origin, target) // 制作一个映射表
// 拿出所有属性,包括可枚举的和不可枚举的,但不能拿到symbol类型
var props = Object.getOwnPropertyNames(origin);
props.forEach((prop, index) => {
if (origin.hasOwnProperty(prop)) {
if (typeof (origin[prop]) === "object") {
if (Object.prototype.toString.call(origin[prop]) == "[object Array]") {
//数组
target[prop] = [];
deepClone(origin[prop], target[prop], hash);
} else if (Object.prototype.toString.call(origin[prop]) == "[object Object]") {
//普通对象
target[prop] = {};
deepClone(origin[prop], target[prop], hash);
} else if (origin[prop] instanceof Date) {
// 处理日期对象
target[prop] = new Date(origin[prop])
} else if (origin[prop] instanceof RegExp) {
// 处理正则对象
target[prop] = new RegExp(origin[prop])
} else {
//null
target[prop] = null;
}
} else if (typeof (origin[prop]) === "function") {
var _copyFn = function (fn) {
var result = new Function("return " + fn)();
for (var i in fn) {
deepClone[fn[i], result[i], hash]
}
return result
}
target[prop] = _copyFn(origin[prop]);
} else {
//除了object、function,剩下都是直接赋值的原始值
target[prop] = origin[prop];
}
}
});
// 单独处理symbol
var symKeys = Object.getOwnPropertySymbols(origin);
if (symKeys.length) {
symKeys.forEach(symKey => {
target[symKey] = origin[symKey];
});
}
return target;
}
比较之下代码问题
- Date,Function等也要克隆
- Symbol类型的没有克隆
优化一下
function cloneDeep(origin, hash = new WeakMap()) {
if (origin == null) return origin;
if (typeof origin === 'symbol') return origin;
if (origin instanceof Date) return new Date(origin);
if (origin instanceof RegExp) return new RegExp(origin);
if (typeof origin === 'function') {
if (/^function/.test(origin.toString()) || /^\(\)/.test(origin.toString())) {
return new Function('return ' + origin.toString())()
}
return new Function('return function ' + origin.toString())()
}
if (origin instanceof RegExp) return new RegExp(origin);
if (typeof origin !== 'object') return origin;
if (hash.has(origin)) return hash.get(origin);
const result = Array.isArray(origin) ? [] : {};
const AllKeys = Object.getOwnPropertyNames(origin)
.concat(Object.getOwnPropertySymbols(origin));
for (let key in AllKeys) {
result[key] = cloneDeep(origin[key], hash);
}
hash.set(origin, result);
return result;
}