在 JavaScript 中,拷貝對象和數組時需要特別注意,因為對象和數組是引用類型,直接賦值只會復制引用,而不是實際的數據。以下是幾種常見的拷貝方法及其應用場景:
1. 淺拷貝(Shallow Copy)
淺拷貝只會復制對象或數組的第一層,而不會遞歸復制其內部的嵌套對象或數組。
1.1 使用 Object.assign
Object.assign
方法用于將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它只會復制第一層屬性。
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = Object.assign({}, obj1);console.log(obj2); // 輸出:{ a: 1, b: { c: 2 } }
console.log(obj2 === obj1); // 輸出:false
console.log(obj2.b === obj1.b); // 輸出:true 如果是深拷貝,這里應該是false。因為b是引用類型的數據
1.2 使用擴展運算符(Spread Operator)
擴展運算符 ...
可以用于對象和數組,實現淺拷貝。
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { ...obj1 };console.log(obj2); // 輸出:{ a: 1, b: { c: 2 } }
console.log(obj2 === obj1); // 輸出:false
console.log(obj2.b === obj1.b); // 輸出:true
1.3 使用 Array.prototype.slice
和擴展運算符
對于數組,可以使用 slice
和擴展運算符實現淺拷貝。
const arr1 = [1, 2, [3, 4]];
const arr2 = [...arr1];console.log(arr2); // 輸出:[1, 2, [3, 4]]
console.log(arr2 === arr1); // 輸出:false
console.log(arr2[2] === arr1[2]); // 輸出:true
2. 深拷貝(Deep Copy)
深拷貝會遞歸復制對象或數組的所有層級,包括嵌套的對象和數組。
2.1 使用 JSON.parse
和 JSON.stringify
JSON.parse
和 JSON.stringify
可以實現簡單的深拷貝,但只適用于不含函數、undefined
、Date
等特殊類型的對象,并且他也無法處理循環引用的問題。
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = JSON.parse(JSON.stringify(obj1));console.log(obj2); // 輸出:{ a: 1, b: { c: 2 } }
console.log(obj2 === obj1); // 輸出:false
console.log(obj2.b === obj1.b); // 輸出:false
2.2 使用遞歸函數
可以編寫一個遞歸函數來實現深拷貝。并且也可以解決循環引用的問題
function deepClone(objValue) {// 定義一個緩存的mapconst cache = new WeakMap() // 為什么用WeakMap 而不用Map,因為WeakMap 可以垃圾回收function _deepClone(obj) {// 判斷是否是非對象類型(即基本數據類型)if (obj === null || (obj instanceof Object === false)) {return obj;}if (cache.has(obj)) {// 新增代碼,查哈希表return cache.get(obj)}let target = Array.isArray(obj) ? [] : {}cache.set(obj, target)// 哈希表設值for (let i in obj) {// 判斷是否是自身屬性,而不是繼承來的屬性if (obj.hasOwnProperty(i)) {if (obj[i] instanceof Object === true) {// 判斷是不是引用類型target[i] = _deepClone(obj[i])} else {target[i] = obj[i]}}}return target;}return _deepClone(objValue)
}
const obj1 = { a: 1, arr:[1,2,3] };
obj1.sub=obj1
obj1.arr.push(obj1)
const newObj= deepClone(obj1);console.log(newObj.arr!==obj1.arr); // true
console.log(newObj.sub!== obj1.sub); // true
console.log(newObj.arr[3]!== obj1); // true
console.log(newObj.arr[3]=== newObj); // true
3. 使用第三方庫
3.1 使用 Lodash 的 _.cloneDeep
Lodash 是一個功能豐富的 JavaScript 實用工具庫,提供了 _.cloneDeep
方法來實現深拷貝。
import _ from 'lodash';const obj1 = { a: 1, b: { c: 2 } };
const obj2 = _.cloneDeep(obj1);console.log(obj2); // 輸出:{ a: 1, b: { c: 2 } }
console.log(obj2 === obj1); // 輸出:false
console.log(obj2.b === obj1.b); // 輸出:false
總結
-
淺拷貝:適用于不需要遞歸復制嵌套對象或數組的場景。
Object.assign
- 擴展運算符
...
Array.prototype.slice
和擴展運算符
-
深拷貝:適用于需要遞歸復制嵌套對象或數組的場景。
JSON.parse
和JSON.stringify
(不支持特殊類型)- 遞歸函數
- 第三方庫(如 Lodash 的
_.cloneDeep
)
根據實際需求選擇合適的拷貝方法,可以高效地實現對象和數組的拷貝。