本文解析JavaScript中浅拷贝与深拷贝的核心区别。浅拷贝仅复制引用地址,修改嵌套对象会影响原数据;深拷贝则创建完全独立的新对象。文章通过Object.assign、展开运算符及JSON序列化等方法,结合递归实现,详细演示了两种拷贝模式的代码实现与注意事项。
前言
在JavaScript中,浅拷贝与深拷贝涉及对象(含数组等)复制时的行为差异:前者仅复制引用地址,后者则创建全新的独立对象。
浅拷贝仅处理基本类型数据,对于复杂类型仅复制其内存指针而不复制对象实体,导致新旧对象共享同一内存空间。若原始对象的属性或子对象发生变动,浅拷贝生成的对象也会随之改变。常见实现方式包括Object.assign()方法、展开运算符(...)以及手动遍历对象属性进行复制。
深拷贝会创建一个与原对象完全一致的新对象,两者不共享内存空间。修改深拷贝产生的新对象不会影响到原始对象。
什么是浅拷贝
执行JavaScript对象或数组的浅拷贝时,系统仅复制基本值的引用,而非完整内容。这意味着浅拷贝仅复制了原始对象属性或元素的引用,并未构建新的对象实例。
例如,假设存在一个简单的对象:
const obj = { name: 'John', age: 30 };随后利用对象赋值语法创建浅拷贝:
const newObj = obj;此时,newObj与obj指向相同的内存地址。若修改newObj的某一属性,obj的对应属性也会同步变更。
newObj.age = 40;
console.log(obj.age); // 输出 40
原因在于newObj与obj指向同一内存地址,因此对任一变量的修改都会反映在另一个变量上。
数组的浅拷贝同样遵循此逻辑:
const arr = [1, 2, 3];
const newArr = arr;
newArr.push(4);
console.log(arr); // 输出 [1, 2, 3, 4]
实现浅拷贝
浅拷贝可通过Object.assign()或展开运算符...来实现;
/**
* 浅拷贝
*
* @param {Object} target 目标对象
* @param {Object} source 源对象
* @return {Object} 返回目标对象
*/
function extend(target, source) {
for (var key in source) {
if (source.hasOwnProperty(key)) {
var value = source[key];
if (typeof value !== 'undefined') {
target[key] = value;
}
}
}
return target;
}
什么是深拷贝
与浅拷贝不同,深拷贝会复制整个对象或数组,包括所有嵌套的子对象和数组。新创建的对象与原始对象完全独立,修改其中一个不会影响另一个。
例如,利用JavaScript内置函数JSON.parse()和JSON.stringify()进行深拷贝:
const obj = { name: 'John', age: 30 };
const newObj = JSON.parse(JSON.stringify(obj));
newObj.age = 40;
console.log(obj.age); // 输出 30
该示例先通过JSON.stringify()将原始对象序列化为字符串,再利用JSON.parse()解析为新对象。由于字符串属于基本类型值而非引用类型,newObj成为一个全新对象,与obj无关联。
数组的深拷贝效果类似:
const arr = [1, [2, 3], [4, [5]]];
const newArr = JSON.parse(JSON.stringify(arr));
newArr[1][0] = 6;
console.log(arr[1][0]); // 输出 2
通过深拷贝创建新数组后,即使修改新数组中嵌套数组的元素,原始数组也不会受到影响。
实现深拷贝
深拷贝可采用JSON.parse(JSON.stringify(obj))、递归复制对象属性或使用lodash等第三方库的deepClone方法。需注意这些方法存在局限,如无法处理函数、循环引用等情况。
function deepCopy(obj) {
var result, copy;
if (obj.constructor === Object)
result = {}; //判断传入的如果是对象,继续遍历
else if (Array.isArray(obj))
result = []; //判断传入的如果是数组,继续遍历
else
return obj; //如果是基本数据类型就直接返回
for (var key in obj) {
if(obj.hasOwnProperty(key)){
copy = obj[key];
if(copy===null){
break;
}
if(typeof copy ==='undefined'){
result[key]=undefined;
}else if (getType(copy) == "Object"||getType(copy) == "Array")
result[key] = deepCopy(copy); //递归方法
else
result[key] = copy; //基本数据类型则赋值给属性
}
}
return result;
}
/**
* 深拷贝
* @param obj
* @param appeard
* @returns {*[]|any}
*/
deepCopy(obj, appeard = new Map()) {
if (!(obj instanceof Object)) return obj; //如果是原始数据类型
if (appeard.has(obj)) return appeard.get(obj); //如果已经出现过
let result = Array.isArray(obj) ? [] : {};
appeard.set(obj, result); //将新对象放入map
//遍历所有属性进行递归拷贝
[...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)].forEach(key => (result[key] = this.deepCopy(obj[key], appeard)));
return result;
}
至此,关于JavaScript浅拷贝与深拷贝原理及实现的解析结束。如需了解更多相关技术内容,可搜索本站往期文章或继续阅读后续章节,感谢支持!