JS深浅拷贝(详细、实用)
存在深浅拷贝的原因
ECMAScript中的数据类型可分为两种:
基本类型:undefined,null,Boolean,String,Number,Symbol
引用类型:Object,Array,Date,Function,RegExp等
不同类型的存储方式:
基本类型:值在栈内存中,基本类型的变量标识符直接与值在内存的地址对应
引用类型:值是一种对象,保存在堆内存中,而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址
不同类型的复制方式:
基本类型:从一个变量向另外一个新变量复制基本类型的值,会创建这个值的一个副本,并将该副本复制给新变量
引用类型:从一个变量向另一个新变量复制引用类型的值,其实复制的是指针,最终两个变量最终都指向同一个对象
简单的方法(有坑)
JSON.stringify():把一个js对象序列化为一个JSON字符串
JSON.parse():把JSON字符串反序列化为一个js对象
let newObj = JSON.parse(JSON.stringify(oldObj));
简单递归版
function deepCopy(obj) {
if (!obj && typeof obj !== 'object') {
throw new Error('error arguments');
}
const targetObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
targetObj[key] = deepCopy(obj[key]);
} else {
targetObj[key] = obj[key];
}
}
}
return targetObj;
}
完美递归版
可以对函数、正则对象、稀疏数组等对象克隆
const clone = parent => {
const parents = [];
const children = [];
const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== 'object') return parent;
let child, proto;
if (isType(parent, 'Array')) {
child = [];
} else if (isType(parent, 'RegExp')) {
// 处理正则对象
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, 'Date')) {
// 处理Date对象
child = new Date(parent.getTime());
} else {
// 处理原型
proto = Object.getPrototypeOf(parent);
// 切断原型链
child = Object.create(proto);
}
// 处理循环引用
const index = parents.indexOf(parent);
if (index != -1) {
// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return children[index];
}
parents.push(parent);
children.push(child);
for (let i in parent) {
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};