1. 简单需求
var obj = {
name: 'tom',
age: 22
}
var p = JSON.parse(JSON.stringfiy(obj))
1.1. JSON的缺点:
- 不支持函数
- 不支持undefined(支持null)
- 不支持循环引用, 比如: a = {name: ‘a’}; a.self = a; a2 = JSON.parse(JSON.stringify(a))
- 不支持date, 会变成ISO8601字符串
- 不支持正则表达式
- 不支持Symbol
- 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
- JSON.stringify()只能序列化对象的可枚举的自有属性
2. 递归拷贝
2.1. 基础版本,未考虑循环引用的问题
/**
*
* @param obj {Object}
*/
function deepClone(obj) {
// 1.判断obj是null及undefined
if (obj == null) return obj;
// 2.判断不是对象就不用拷贝了
if (typeof obj !== 'object') return obj;
// 3.判断是Date就返回一个新的Date,因为日期也是对象
if (obj instanceof Date) return new Date(obj);
// 4.判断是正则,就返回一个新的正则,因为正则也是对象
if (obj instanceof RegExp) return new RegExp(obj);
// 5.不是数组就是对象
// 注意:new obj.constructor 要不是[], 要不是{}
let newObj = new obj.constructor;
for (let key in obj) {
// 如果不是原型链上的属性,再拷贝
if (obj.hasOwnProperty(key)) {
// 实现递归拷贝
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
};
let obj = { age: 18, name: { firstName: 'jerry' } };
let result = deepClone(obj);
obj.age = 100;
console.log(result);
2.2. 升级版,利用WeakMap解决循环引用的问题
function deepClone(obj, hash=new WeakMap()) {
// 1.判断obj是null及undefined
if (obj == null) return obj;
// 2.判断不是对象就不用拷贝了
if (typeof obj !== 'object') return obj;
// 3.判断是Date就返回一个新的Date,因为日期也是对象
if (obj instanceof Date) return new Date(obj);
// 4.判断是正则,就返回一个新的正则,因为正则也是对象
if (obj instanceof RegExp) return new RegExp(obj);
// 如果WeakMap中有对象就直接返回
if (hash.has(obj)) return hash.get(obj);
// 5.不是数组就是对象
// 注意:new obj.constructor 要不是[], 要不是{}
let newObj = new obj.constructor;
// 如果是对象就放到hash里,再次拷贝这个对象就存在了,那么就直接返回这个对象
hash.set(obj, newObj);
for (let key in obj) {
// 如果不是原型链上的属性,再拷贝
if (obj.hasOwnProperty(key)) {
// 实现递归拷贝
newObj[key] = deepClone(obj[key], hash);
}
}
return newObj;
};
let obj = { age: 18, name: { firstName: 'jerry' } };
let result = deepClone(obj);
obj.age = 100;
console.log(result);
2.3. 代码优化,最终版本
const isObject = (obj) => typeof obj === 'object' && obj !== null;
function deepClone(obj, hash = new WeakMap()) {
if (!isObject(obj)) {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (hash.has(obj)) {
return hash.get(obj);
}
let newObj = new obj.constructor();
hash.set(obj, newObj);
if (Array.isArray(obj)) {
obj.forEach((item, index) => {
newObj[index] = deepClone(item, hash);
});
} else {
Object.getOwnPropertyNames(obj).forEach((key) => {
newObj[key] = deepClone(obj[key], hash);
});
Object.getOwnPropertySymbols(obj).forEach((key) => {
newObj[key] = deepClone(obj[key], hash);
});
}
return newObj;
}
let obj = { age: 18, name: { firstName: 'jerry' } };
// let obj = [{ age: 18, name: { firstName: 'jerry' } }];
let result = deepClone(obj);
obj.age = 100;
console.log(result);
下次再见