在 JavaScript 中,拷贝对象和数组时需要特别注意,因为对象和数组是引用类型,直接赋值只会复制引用,而不是实际的数据。以下是几种常见的拷贝方法及其应用场景:
浅拷贝只会复制对象或数组的第一层,而不会递归复制其内部的嵌套对象或数组。
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是引用类型的数据
扩展运算符 ...
可以用于对象和数组,实现浅拷贝。
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
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
深拷贝会递归复制对象或数组的所有层级,包括嵌套的对象和数组。
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
可以编写一个递归函数来实现深拷贝。处理循环引用问题
function deepClone(objValue) {
// 定义一个缓存的map
const 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(deepClone(undefined))
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
_.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
(不支持特殊类型)_.cloneDeep
)根据实际需求选择合适的拷贝方法,可以高效地实现对象和数组的拷贝。