浅拷贝:只拷贝基本类型的数据,引用类型的数据拷贝后也会发生引用,因为拷贝的是对象的引用地址。
深拷贝:在堆中重新分配内存,并把源对象中所有属性都新建拷贝,新旧对象完全隔离,互不影响。
const user = {
name: '下雪天的夏风'
}
const user2 = user
user.name = 'xxx'
console.log(user); // { name: 'xxx' }
console.log(user2); // { name: 'xxx' }
console.log(user === user2); // true
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = Object.assign({}, user);
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '北京', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = { ...user };
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '北京', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
数组元素是引用类型时,同样拷贝的是引用地址。
const arr1 = [1, true, "Hello", { name: "下雪天的夏风", age: 18 }];
// slice() 替换为 concat() 结果一致。
const arr2 = arr1.slice();
arr2[0] = 2;
arr2[3].age = 36;
console.log(arr1); // [ 1, true, 'Hello', { name: '下雪天的夏风', age: 36 } ]
console.log(arr2); // [ 2, true, 'Hello', { name: '下雪天的夏风', age: 36 } ]
在 jQuery 中,$.extend([deep], target, object1, [objectN])
方法可进行深浅拷贝。jquery 中文文档
true
为深拷贝,默认为 false
浅拷贝<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = {};
$.extend(user2, user);
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '北京', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
</script>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script>
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = _.clone(user);
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '北京', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
</script>
遇到的问题和使用建议后面会介绍。
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = JSON.parse(JSON.stringify(user));
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '陕西', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
全局属性。浏览器环境是 window 的属性,node 环境是 global 的属性。
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = structuredClone(user);
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '陕西', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = {};
$.extend(true, user2, user);
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '陕西', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
</script>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script>
const user = {
name: "下雪天的夏风",
address: {
province: "陕西",
city: "西安",
},
};
const user2 = _.cloneDeep(user);
user2.name = "xxx";
user2.address.province = "北京";
console.log(user); // { name: '下雪天的夏风', address: { province: '陕西', city: '西安' } }
console.log(user2); // { name: 'xxx', address: { province: '北京', city: '西安' } }
</script>
可以看到,上面的例子中使用的都是简单的基本数据类型和普通对象,这也是日常开发中最常见的。
但还有一些特殊情况,比如函数在进行深拷贝时,其实和基本类型一样直接拷贝即可。但函数不支持序列化和结构化算法!所以 JSON.parse(JSON.stringify)
和 structuredClone()
无法处理函数,所以源对象中有函数时会有问题!
const user = {
getAge() {
console.log(18);
},
};
const user2 = JSON.parse(JSON.stringify(user));
console.log(user2); // {},函数直接被忽略
const user = {
getAge() {
console.log(18);
},
};
// 结构化算法无法处理函数。
// Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': getAge()
const user2 = window.structuredClone(user);
还有一些特殊的对象,比如日期对象,正则对象,包装类型,Map,Set 等,在拷贝时都需要特殊处理。
在生成环境中的建议:
lodash.cloneDeep()
或 $.extend()
。特殊的类型就不考虑了,主要是完善下 JSON.parse(JSON.stringify)
不支持函数的拷贝。Map 和 Set 其实也是判断后遍历递归即可。
function deepClone(target) {
let result;
// 基本类型、函数,直接赋值。
if (typeof target !== "object" || typeof target === null) {
result = target;
} else {
result = Array.isArray(target) ? [] : {};
for (const key in target) {
result[key] = deepClone(target[key]);
}
}
return result;
}
以上。