面试中优先考虑写这个:
function deepClone(obj) {
// 基础数据类型,直接返回
if (obj === null || typeof obj !== "object") {
return obj;
}
// 引用类型,进一步区分数组和对象
let target = Array.isArray(obj) ? [] : {};
// 使用for...in循环,递归拷贝
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
target[key] = deepClone(obj[key]);
}
}
return target;
}
如果要求处理循环引用的,可以这么做:
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== "object") {
return obj;
}
// 出现了循环引用,则返回原对象
if (hash.has(obj)) {
return hash.get(obj);
}
let target = Array.isArray(obj) ? [] : {};
// 哈希表记录出现过的对象
hash.set(obj, target);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 从始至终只有初始创建的一个WeakMap对象(哈希表)
target[key] = deepClone(obj[key], hash);
}
}
return target;
}
这里使用 WeakMap 而不是 Map 是为了避免内存泄漏。
日常开发
对于结构相对简单的纯数据对象(不包含函数、Date等特殊类型),且没有循环引用时,直接使用:
let target = JSON.parse(JSON.stringify(obj));
对象包含特殊类型对象,最全面的深拷贝,项目中直接使用lodash库提供的cloneDeep
import _ from 'lodash;
let target = _.cloneDeep(obj);