Deep copy
Involving interview questions: What is a shallow copy? How to achieve shallow copy? What is a deep copy? How to achieve deep copy?
let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
Shallow copy
First of all, you can Object.assign
solve this problem. Many people think that this function is used for deep copy. In fact, it is not. It Object.assign
will only copy all the attribute values to the new object. If the attribute value is an object, the address is copied, so it is not a deep copy.
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
In addition, we can also ...
implement shallow copy by spreading the operator
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
Usually shallow copy can solve most of the problems, but when we encounter the following situations, we may need to use deep copy
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native
ps:
arr.slice(0)
arr.concat()
let obj2 = {... obj}
Shallow copy only solves the problem of the first layer. If there are still objects in the following values, then it is back to the original topic, and both share the same address. To solve this problem, we have to use deep copy
(2) Deep copy
This problem can usually be solved through JSON.parse(JSON.stringify(object))
.
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
But this method also has limitations:
- Will ignore
undefined
- Will ignore
symbol
- Cannot serialize function
- Cannot resolve circularly referenced objects
let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
If you have such a circular reference object, you will find that deep copy cannot be achieved through this method
When encountering a function, undefined
or symbol
, the object cannot be serialized normally
let a = {
age: undefined,
sex: Symbol('male'),
jobs: function() {},
name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}
You will find that in the above cases, the method ignores the function sum undefined
.
But under normal circumstances, complex data can be serialized, so this function can solve most problems.
If the object you need to copy contains built-in types and does not contain functions, you can use MessageChannel
function structuralClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port2.onmessage = ev => resolve(ev.data)
port1.postMessage(obj)
})
}
var obj = {
a: 1,
b: {
c: 2
}
}
obj.b.d = obj.b
// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
const test = async () => {
const clone = await structuralClone(obj)
console.log(clone)
}
test()
Of course, you may want to implement a deep copy by yourself, but in fact it is very difficult to implement a deep copy. We need to consider a variety of boundary conditions, such as how the prototype chain is processed, how the DOM is processed, etc., so the deep copy we implemented here is just Simple version, and I actually recommend using lodash's deep copy function .
function deepClone(obj){ //判断参数是不是一个对象
let objClone = new obj.constructor();
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key]; }
}
}
}
return objClone;
}