一、核心區別
特性 | 淺拷貝 | 深拷貝 |
---|---|---|
復制層級 | 僅復制對象的第一層屬性 | 遞歸復制對象的所有層級屬性(包括嵌套對象和數組) |
引用關系 | 嵌套對象/數組與原對象共享內存(引用拷貝) | 嵌套對象/數組與原對象完全獨立(值拷貝) |
修改影響 | 修改拷貝后的嵌套屬性會影響原對象 | 修改拷貝后的任何屬性都不會影響原對象 |
適用場景 | 簡單數據結構,無需嵌套獨立性 | 復雜數據結構,要求完全獨立 |
二、淺拷貝實現方案
1. 對象淺拷貝
Object.assign()
const obj = { a: 1, b: { c: 2 } }; const shallowCopy = Object.assign({}, obj);
- 展開運算符(
...
)const obj = { a: 1, b: { c: 2 } }; const shallowCopy = { ...obj };
2. 數組淺拷貝
Array.prototype.slice()
const arr = [1, 2, [3, 4]]; const shallowCopy = arr.slice();
- 展開運算符(
...
)const arr = [1, 2, [3, 4]]; const shallowCopy = [...arr];
三、深拷貝實現方案
1. JSON 序列化法
原理:通過 JSON.stringify()
和 JSON.parse()
序列化對象。
優點:簡單快捷,適合純數據對象。
缺點:無法處理函數、undefined
、Symbol
、Date
、RegExp
、循環引用等。
const obj = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(obj));
2. 遞歸深拷貝(手動實現)
原理:遞歸遍歷對象屬性,處理所有數據類型。
優點:可自定義邏輯,支持復雜場景。
缺點:需處理邊界條件(如循環引用)。
function deepClone(source, hash = new WeakMap()) {// 處理基本類型和 null/undefinedif (source === null || typeof source !== 'object') return source;// 處理循環引用if (hash.has(source)) return hash.get(source);// 處理 Date 和 RegExpif (source instanceof Date) return new Date(source);if (source instanceof RegExp) return new RegExp(source);// 初始化拷貝對象(保持原型鏈)const target = new source.constructor();hash.set(source, target);// 遍歷所有屬性(包括 Symbol)Reflect.ownKeys(source).forEach(key => {target[key] = deepClone(source[key], hash);});return target;
}
3. 第三方庫實現
-
Lodash(推薦)
import { cloneDeep } from 'lodash'; const deepCopy = cloneDeep(obj);
-
jQuery
const deepCopy = $.extend(true, {}, obj);
四、特殊場景處理
1. 循環引用
問題:對象屬性間接引用自身,導致遞歸棧溢出。
解決方案:使用 WeakMap
緩存已拷貝對象。
const obj = { a: 1 };
obj.self = obj; // 循環引用
const copy = deepClone(obj); // 遞歸實現中通過 WeakMap 避免死循環
2. 處理特殊對象
Date
對象:通過new Date(source.getTime())
重建。RegExp
對象:通過new RegExp(source.source, source.flags)
重建。Map
/Set
:遍歷并遞歸拷貝每個元素。
3. 函數拷貝
問題:函數無法被完全拷貝(可能依賴閉包環境)。
解決方案:通過 eval
或 new Function
重建函數(需謹慎使用)。
五、性能對比與選型建議
方法 | 性能 | 功能完整性 | 安全性 |
---|---|---|---|
JSON 法 | 高 | 低(丟失類型) | 安全 |
遞歸實現 | 中 | 高 | 需處理邊界 |
Lodash | 中 | 高 | 安全 |
選型建議:
- 純數據對象且無特殊類型 → JSON 法。
- 復雜對象或需要保留類型 → Lodash 的 cloneDeep。
- 定制化需求 → 手動遞歸實現。
六、驗證示例
const origin = { a: 1, b: { c: 2 }, d: new Date(), e: /regex/, f: function() { console.log('test') },g: [1, 2, { h: 3 }]
};
origin.self = origin; // 循環引用const copy = deepClone(origin);// 驗證
console.log(copy.b === origin.b); // false(深拷貝成功)
console.log(copy.d instanceof Date); // true(保留類型)
console.log(copy.self === copy); // true(循環引用處理成功)