const obj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj };
obj.b.c = 3;
console.log(shallowCopy.b.c); // 3 - 被修改了!
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, obj);
obj.b.c = 3;
console.log(shallowCopy.b.c); // 3
// 1. 扩展运算符
const arr = [1, [2, 3], { a: 4 }];
const copy1 = [...arr];
// 2. slice()
const copy2 = arr.slice();
// 3. concat()
const copy3 = [].concat(arr);
// 嵌套对象仍然共享引用
arr[1][0] = 99;
console.log(copy1[1][0]); // 99
const obj = {
a: 1,
b: { c: 2 },
date: new Date(),
func: function () {
return 1;
},
undef: undefined,
symbol: Symbol("foo"),
infinity: Infinity,
nan: NaN,
};
const deepCopy = JSON.parse(JSON.stringify(obj));
console.log(deepCopy);
// {
// a: 1,
// b: { c: 2 },
// date: "2024-01-01T00:00:00.000Z", // Date对象转为字符串
// // func 丢失(函数无法序列化)
// // undef 丢失(undefined无法序列化)
// // symbol 丢失
// infinity: null, // Infinity 转为 null
// nan: null // NaN 转为 null
// }
JSON 方法的缺点:
function deepClone(obj, hash = new WeakMap()) {
// 处理基本类型和null
if (obj === null || typeof obj !== "object") {
return obj;
}
// 处理Date
if (obj instanceof Date) {
return new Date(obj);
}
// 处理数组
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item, hash));
}
// 处理正则表达式
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 处理Map
if (obj instanceof Map) {
const map = new Map();
obj.forEach((value, key) => {
map.set(key, deepClone(value, hash));
});
return map;
}
// 处理Set
if (obj instanceof Set) {
const set = new Set();
obj.forEach(value => {
set.add(deepClone(value, hash));
});
return set;
}
// 处理循环引用
if (hash.has(obj)) {
return hash.get(obj);
}
// 处理普通对象
const cloneObj = Object.create(Object.getPrototypeOf(obj));
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
// 处理Symbol属性
const symbolKeys = Object.getOwnPropertySymbols(obj);
for (let symKey of symbolKeys) {
cloneObj[symKey] = deepClone(obj[symKey], hash);
}
return cloneObj;
}
// 使用示例
const original = {
name: "John",
address: { city: "New York" },
hobbies: ["reading", "coding"],
date: new Date(),
regExp: /test/gi,
map: new Map([["key", "value"]]),
set: new Set([1, 2, 3]),
[Symbol("id")]: 123,
};
original.self = original; // 循环引用
const cloned = deepClone(original);
console.log(cloned !== original); // true
console.log(cloned.address !== original.address); // true
console.log(cloned.self === cloned); // true - 保持了循环引用结构
// Lodash
import _ from "lodash";
const cloned = _.cloneDeep(obj);
// jQuery
const cloned = jQuery.extend(true, {}, obj);
// structuredClone(现代浏览器原生API)
const cloned = structuredClone(obj);
const original = {
name: "Alice",
address: { city: "Paris" },
date: new Date(),
array: [1, 2, 3],
map: new Map([["key", "value"]]),
set: new Set([1, 2, 3]),
// 注意:函数、DOM节点、原型链等不能复制
};
const cloned = structuredClone(original);
console.log(cloned.date instanceof Date); // true
console.log(cloned.map instanceof Map); // true
// 支持循环引用
original.self = original;
const clonedWithCycle = structuredClone(original);
console.log(clonedWithCycle.self === clonedWithCycle); // true
const obj = { a: 1 };
obj.self = obj;
// 正确处理循环引用的深拷贝
function cloneWithCycle(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== "object") return obj;
if (hash.has(obj)) return hash.get(obj);
const clone = Array.isArray(obj) ? [] : {};
hash.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = cloneWithCycle(obj[key], hash);
}
}
return clone;
}
// 函数拷贝(通常是引用,但可以创建新函数)
function cloneFunction(func) {
// 方法1:使用 Function 构造函数
const funcString = func.toString();
const clone = new Function("return " + funcString)();
// 复制属性
for (let key in func) {
if (func.hasOwnProperty(key)) {
clone[key] = func[key];
}
}
return clone;
}
// 使用示例
function originalFunc(x) {
return x * 2;
}
originalFunc.customProp = "test";
const clonedFunc = cloneFunction(originalFunc);
console.log(clonedFunc(5)); // 10
console.log(clonedFunc.customProp); // 'test'
// 使用缓存提高性能
function deepCloneOptimized(obj, cache = new WeakMap()) {
// 基础类型直接返回
if (obj === null || typeof obj !== "object") {
return obj;
}
// 检查缓存
if (cache.has(obj)) {
return cache.get(obj);
}
// 获取构造器
const Constructor = obj.constructor;
// 处理特殊类型
switch (Constructor) {
case Date:
return new Date(obj);
case RegExp:
return new RegExp(obj);
case Map:
return new Map(Array.from(obj, ([k, v]) => [k, deepCloneOptimized(v, cache)]));
case Set:
return new Set(Array.from(obj, v => deepCloneOptimized(v, cache)));
case Array:
case Object:
const clone = new Constructor();
cache.set(obj, clone);
// 普通属性和Symbol属性
const allKeys = [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)];
for (let key of allKeys) {
clone[key] = deepCloneOptimized(obj[key], cache);
}
return clone;
default:
// 处理其他构造函数创建的对象
return obj;
}
}
// 场景1:修改顶层属性,保持嵌套对象引用
const config = {
apiUrl: "https://api.example.com",
options: { timeout: 5000, retry: 3 }, // 共享给所有副本
};
const userConfig = { ...config, userId: 123 };
// userConfig.options 和 config.options 是同一个对象
// 场景2:性能敏感,对象简单无嵌套
const simpleObj = { x: 1, y: 2 };
const copy = { ...simpleObj }; // 浅拷贝足够
// 场景1:状态管理(React/Vue等)
const initialState = {
user: {
profile: { name: "John", age: 30 },
settings: { theme: "dark" },
},
cart: [{ id: 1, quantity: 2 }],
};
function reducer(state, action) {
switch (action.type) {
case "UPDATE_PROFILE":
// 需要深拷贝来保证不变性
return deepClone(state);
default:
return state;
}
}
// 场景2:撤销/重做功能
class HistoryManager {
constructor() {
this.history = [];
this.currentIndex = -1;
}
pushState(state) {
// 保存深拷贝的状态
this.history = this.history.slice(0, this.currentIndex + 1);
this.history.push(deepClone(state));
this.currentIndex++;
}
undo() {
if (this.currentIndex > 0) {
this.currentIndex--;
return deepClone(this.history[this.currentIndex]);
}
}
}
// 测试不同拷贝方法的性能
const testObj = createDeepObject(5, 5); // 5层深度,每层5个属性
console.time("JSON方法");
const jsonCopy = JSON.parse(JSON.stringify(testObj));
console.timeEnd("JSON方法");
console.time("递归深拷贝");
const recursiveCopy = deepClone(testObj);
console.timeEnd("递归深拷贝");
console.time("structuredClone");
const nativeCopy = structuredClone(testObj);
console.timeEnd("structuredClone");
// 选择建议:
// 1. 简单数据(可序列化) -> JSON方法(最快)
// 2. 复杂数据+现代浏览器 -> structuredClone
// 3. 需要特殊类型支持 -> 自定义深拷贝
如果只有一层结构,用浅拷贝
如果有嵌套且需要独立,用深拷贝