00⭐⭐(!!)深拷贝与浅拷贝
(只谈论了 基本数据类型 和 ES5 的引用数据类型)
(关于 ES6 及之后 新增的Symbol,Map 等数据类型还有待谈论)
(以下仅为 原生JS 的方法,未涉及第三方库使用,第三方库lodash
也有函数可实现)
注意:
- ⭐浅拷贝 与 深拷贝 都是需要开辟新内存地址,产生新对象
- 但是
- 浅拷贝 只拷贝 原始对象中的
最外部一层
- 深拷贝 则会拷贝 原始对象中
所有的层级
Eg:
对于下面这个 对象
const hotGirl = {
name: 'Maji',
age: 28,
info: {
desc: 'superHot',
},
};
- 浅拷贝:
- 只能拷贝属性
name
和age
拥有新地址,但是 拷贝到 info时 则会引用原始对象 中的地址 - (!!!即改变原始对象中的
info.desc
的值时,浅拷贝出来的新对象的info.desc
的值同样会改变), - 属于
不彻底拷贝
,所以 只有一层属性的对象 适合浅拷贝。
- 只能拷贝属性
- 深拷贝
- 则可以 完全 拷贝所有层级,变成一个拥有 新地址 的 新对象。属于
彻底的拷贝
- 则可以 完全 拷贝所有层级,变成一个拥有 新地址 的 新对象。属于
浅拷贝:只拷贝最外面一层的数据-(不彻底的拷贝)
一 使用Object.assign(target,source01,source02,...)
-
语法:(将
source对象
中的值 拷贝到target目标对象
中) -
target
可以写成空对象
-
**注意:**若target非空 ,且source 和 target中有重复属性 ,则target中的属性会被覆写;target的值会被改变, 但是source的值不会
-
// obj2 拿到的就是 target let obj2 = Object.assign(target,source)
Eg: (得到新的对象)
const hotGirl = {
name: 'Maji',
age: 28,
};
// target `可以写成空对象`
let newHotGirl = Object.assign({},hotGirl)
console.log(newHotGirl)
// {name: 'Maji', age: 28}
Eg (重新赋值给目标对象)
const hotGirl = {
name: 'Maji',
age: 28,
};
const pureGirl = {
name: 'Olla',
age: 26,
tall:"180cm"
};
// 让Olla 从 pureGirl 成为 hotGirl
// 注意这里 hotGirl 的属性被改变了,pureGirl没有
Object.assign(hotGirl,pureGirl)
// target `可以写成空对象`
console.log(hotGirl)
// {name: 'Olla', age: 26, tall: '180cm'}
二 使用扩展语法 (...)
- 使用简单,不必多说
const hotGirl = {
name: 'Maji',
age: 28,
};
let newHotGirl = { ...hotGirl }
console.log(newHotGirl)
//{name: 'Maji', age: 28}
三 for…in 循环拷贝
const hotGirl = {
name: 'Maji',
age: 28,
};
const newHotGirl = {};
// 用 for in 将 obj1 的值拷贝给 obj2
for (let key in hotGirl) {
//字符串 赋值 就是开辟 新的内存地址,成为完全的新值
newHotGirl[key] = hotGirl[key];
}
console.log(newHotGirl)
//{name: 'Maji', age: 28}
深拷贝: 每一层数据都会拷贝-(最彻底的拷贝)
一 JSON方法
- 语法:
let newObj = JSON.parse(JSON.stringify(obj))
缺陷(暂不谈论,死记)
- 会忽略
undefined
- 会忽略
symbol
- 会忽略函数:
JSON.stringify()
会默认移除函数。 JSON.stringify()
无法拷贝Map、Set、RegExp
这些特殊数据类型。- 不能序列化函数 ???
- 不能解决循环引用的对象 见[前端进阶之道-深浅拷贝](JS | 前端进阶之道 (yuchengkai.cn))
二 ⭐⭐ JS 有原生的深拷贝 API structuredClone
[MDN—structuredClone()](structuredClone() - Web 开发技术 | MDN (mozilla.org))
-
结构化拷贝算法的实现,能够实现几乎对所有数据类型的深拷贝
-
该API 较新,兼容要考虑(目前主流都支持) ,虽然有些缺点,但是不影响,放心用,以后应该会加大支持力度
- 可引入
core-js
兼容库 core-js#structuredclone,兼容旧版本。
- 可引入
-
相比
JSON.parse()
,structuredClone API
的性能更好 -
语法:
-
let hotGirl = { age: 1, info: { cup: 'E' } } let newHotGirl = structuredClone(hotGirl); console.log(newHotGirl)
缺点:(死记,放心用,以后会加大支持力度)
-
原型:无法拷贝对象的原型链。
-
函数:无法拷贝函数。(普通函数、箭头函数、类、方法)
-
let hotGirl = { age: 1, info: { cup: 'E' }, f:function(){ console.log('hot') } } let newHotGirl = structuredClone(hotGirl); console.log(newHotGirl)
-
-
DOM节点
-
不可克隆:并没有支持所有类型的拷贝,比如
Error
。
三 for … in 手写
完整源码
// 手写深拷贝
export function deepClone(obj) {
//判断 传入对象 为 数组 或者 对象
var result = Array.isArray(obj) ? [] : {};
// for in 遍历
for (var key in obj) {
// 判断 是否 为自身 的属性值(排除原型链干扰)
if (obj.hasOwnProperty(key)) {
// 判断 对象的属性值 中 存储的 数据类型 是否为对象
if (typeof obj[key] === 'object') {
// 有可能等于 null
if (obj[key] === null ) {
result[key] = null
continue
}
// 递归调用
result[key] = deepClone(obj[key]); //递归复制
}
// 不是的话 直接 赋值 copy
else {
result[key] = obj[key];
}
}
}
// 返回 新的对象
return result;
}
补充说明
- 2022 09 14 在
if (typeof obj[key] === 'object')'
之内增加判断null 操作;修复之前(将null 拷贝成 {}的问题)
if (typeof obj[key] === 'object') {
// 有可能等于 null
if (obj[key] === null ) {
result[key] = null
continue
}
// 递归调用
result[key] = deepClone(obj[key]); //递归复制
}