对象拷贝
2021-04-09 本文已影响0人
行走的蛋白质
一. 深拷贝 / 浅拷贝
1. 浅拷贝: 指针拷贝, 让拷贝前和拷贝后对象的指针指向同一块内存地址
- 增加了原对象的引用计数
- 没有新的内存分配
let obj1 = {
a: 1,
b: 2
}
let obj2 = obj1
obj1.a = 3
console.log(obj2.a) // 3
2. 深拷贝
- Object.assign(),只能拷贝一层, 且会改变原有对象 - ( 数组 concat 方法不会改变原有数组 )
- ES6 扩展运算符 { ...obj },同样只能拷贝一层
- immutable.js 实现,利用其不可变数据集的特性,省去深拷贝环节每次改变只影响当前节点和它的父节点, 其他节点复用,效率高
- lodash 库的 cloneDeep 方法实现深拷贝
- 遍历 for in / Object.keys(obj) 递归, 代码以及优缺点如下:
1). JSON.parse(JSON.stringify()) 实现深拷贝的不足
- 如果 obj 里面有时间对象,则 JSON.stringify 后再 JSON.parse 的结果,时间将只是字符串的形式。而不是时间对象;
const obj = {
date: new Date()
}
const newObj = JSON.parse(JSON.stringify(obj))
console.log(Object.prototype.toString.call(obj.date)) // [object Date]
console.log(Object.prototype.toString.call(newObj.date)) // [object String]
console.log(newObj.date) // 2021-04-09T02:12:31.440Z
- 如果 obj 里有 RegExp、Error 对象,则序列化的结果将只得到空对象;
const obj = {
date: new Date(),
regExp: new RegExp('\d'),
error: new Error('error')
}
const newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj.regExp) // {}
console.log(newObj.error) // {}
- 如果 obj 键名有 Symbol 或者值里有函数,undefined,则序列化的结果会丢失;
const obj = {
date: new Date(),
regExp: new RegExp('\d'),
error: new Error('error'),
fn: function() {
console.log('function')
},
und: undefined
}
const sym = Symbol('sym')
test[sym] = 'sym'
const newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj) // {date: "2021-04-10T01:47:04.806Z", regExp: {…}, error: {…}}
- 如果 obj 里有 NaN、Infinity 和 -Infinity,则序列化的结果会变成 null
const obj = {
date: new Date(),
regExp: new RegExp('\d'),
error: new Error('error'),
fn: function() {
console.log('function')
},
und: undefined,
nan: NaN,
infi: Infinity
}
const newObj = JSON.parse(JSON.stringify(obj))
console.log(Object.prototype.toString.call(obj.date))
console.log(Object.prototype.toString.call(newObj.date))
console.log(newObj.nan) // null
console.log(newObj.infi) // null
- JSON.stringify() 只能序列化对象的可枚举的自有属性,例如 如果 obj 中的对象是有构造函数生成的, 则使用 JSON.parse(JSON.stringify(obj)) 深拷贝后,会丢弃对象的 constructor;
function Person(name) {
this.name = name
}
const zs = new Person('zhangsan')
const obj = {
date: new Date(),
regExp: new RegExp('\d'),
error: new Error('error'),
fn: function() {
console.log('function')
},
und: undefined,
nan: NaN,
infi: Infinity,
zs
}
const newObj = JSON.parse(JSON.stringify(obj))
console.log(obj.zs)
console.log(newObj.zs)
![](https://img.haomeiwen.com/i17067702/59926e9b455a1447.png)
- 如果对象中存在循环引用的情况也无法正确实现深拷贝;
![](https://img.haomeiwen.com/i17067702/8ac122be71fee77b.png)
2). Reflect 递归实现
function deepClone(obj) {
// 判断如果 obj 是基本类型数据或者函数, 直接返回
if (!obj || typeof obj !== 'object') return obj;
if (obj instanceof Error) {
return new Error(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (obj instanceof Date) {
return new Date(obj);
}
const result = Array.isArray(obj) ? [] : {};
Reflect.ownKeys(obj).forEach(item => {
if (obj[item] && typeof obj[item] === 'object') {
result[item] = deepClone(obj[item]);
} else {
result[item] = obj[item];
}
})
return result;
}
// 测试
const a = '111'
console.log(deepClone(a))
const test = {
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: {
name: '我是一个对象',
num: 1,
id: 1
},
arr: [0, 1, 2],
func: function() {
console.log('我是一个函数')
},
date: new Date(0),
reg: new RegExp('我是一个正则', 'ig'),
err: new Error('error')
}
const sym = Symbol('我是一个Symbol')
test[sym] = 'Symbol'
console.log(deepClone(test))
console.log(deepClone(test.date))
console.log(deepClone(test.reg))
console.log(deepClone(test.err))
![](https://img.haomeiwen.com/i17067702/6e0c7f2ec7a80a39.png)
- 递归方法深拷贝互相引用的对象会造成栈溢出