克隆
对象深度克隆的简单实现
function deepClone(obj) {
var newObj = obj instanceof Array ? [] : {};
for (var item in obj) {
var temple =
typeof obj[item] == "object" ? deepClone(obj[item]) : obj[item];
newObj[item] = temple;
}
return newObj;
}
ES5 的常用的对象克隆的一种方式。注意数组是对象,但是跟对象又有一定区别,所以 我们一开始判断了一些类型,决定 newObj 是对象还是数组。
实现 JS 中所有对象的深度克隆(包装对象,Date 对象,正则对象)
function deepClone(obj, hash = new WeakMap()) {
// 处理 null 或非对象类型
if (Object(obj) !== obj) return obj;
// 处理 Date
if (obj instanceof Date) return new Date(obj);
// 处理 RegExp
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
// 处理包装对象(Number / String / Boolean / Symbol)
const primitives = [Number, String, Boolean, Symbol];
for (const Type of primitives) {
if (obj instanceof Type) return Object(Type.prototype.valueOf.call(obj));
}
// 处理 Map
if (obj instanceof Map) {
const copy = new Map();
hash.set(obj, copy);
obj.forEach((value, key) => copy.set(deepClone(key, hash), deepClone(value, hash)));
return copy;
}
// 处理 Set
if (obj instanceof Set) {
const copy = new Set();
hash.set(obj, copy);
obj.forEach((value) => copy.add(deepClone(value, hash)));
return copy;
}
// 处理数组或普通对象
const copy = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
hash.set(obj, copy);
// 递归拷贝可枚举属性,包括 Symbol 键
Reflect.ownKeys(obj).forEach((key) => {
copy[key] = deepClone(obj[key], hash);
});
return copy;
}
实现要点:
- 用
WeakMap缓存已拷贝对象,避免循环引用导致的栈溢出。 - 不可变对象(
Date、RegExp、包装对象)直接构造新实例返回。 - 容器类型(
Map、Set)递归拷贝其内部元素。 - 使用
Reflect.ownKeys遍历,覆盖普通字符串键和Symbol键。 - 通过
Object.create(Object.getPrototypeOf(obj))保留原对象的原型链。
深浅拷贝的区别和实现
浅拷贝(Shallow Copy) 只复制对象的第一层属性,如果属性值是引用类型,则只拷贝引用地址,新旧对象仍共享同一份内层数据。深拷贝(Deep Copy) 则会递归复制所有层级,生成一个与原对象完全独立的新对象,互不影响。
| 对比项 | 浅拷贝 | 深拷贝 |
|---|---|---|
| 复制层级 | 仅第一层 | 所有层级 |
| 引用类型属性 | 共享同一引用 | 独立副本 |
| 修改内层对象是否相互影响 | 是 | 否 |
| 实现成本 | 低 | 较高 |
浅拷贝的常见实现方式:
// 1. Object.assign
const shallow1 = Object.assign({}, source);
// 2. 展开运算符
const shallow2 = { ...source };
// 3. Array.prototype.slice / concat(针对数组)
const arr = [1, 2, { a: 3 }];
const shallow3 = arr.slice();
深拷贝的常见实现方式:
// 1. JSON 序列化(简单但有局限:无法处理函数、undefined、Symbol、循环引用、Date、RegExp 等)
const deep1 = JSON.parse(JSON.stringify(source));
// 2. structuredClone(原生 API,支持循环引用、Date、Map、Set 等,但不支持函数、DOM 节点)
const deep2 = structuredClone(source);
// 3. 手写递归实现(可参考上文 deepClone 函数,支持更多类型和边界情况)
实际开发中:若数据结构简单且无函数/循环引用,JSON.parse(JSON.stringify()) 最快捷;若需处理更复杂的类型,使用 structuredClone 或手写递归克隆。上文给出的 deepClone 函数已经覆盖了 Date、RegExp、包装对象、Map、Set 以及循环引用等情况,是比较通用的实现。