1.現(xiàn)象
一句話解釋下它倆的區(qū)別:
假設(shè)B復(fù)制了A,當(dāng)修改A時,看B是否會發(fā)生變化,如果B也跟著改變,說明這是淺拷貝;如果B沒變,那就是深拷貝。
實際開發(fā)中遇到的其實就是這樣疑惑:
let a = {name: '我是A'}
let b = a
console.log(a) // {name: "我是A"}
console.log(b) // {name: "我是A"}
b.name = '我是B'
console.log(a) // {name: "我是B"}
console.log(b) // {name: "我是B"}
/* 為什么 a 的值也發(fā)生變化? */
這里需要解釋下js中數(shù)據(jù)類型 以及堆棧
2.數(shù)據(jù)類型
基本數(shù)據(jù)類型
String, Number, Boolean, Null, Undefined, Symbol
引用數(shù)據(jù)類型
Object, Array, Function, RegExp, Date 等
基本數(shù)據(jù)——類型它們鍵值都存在棧內(nèi)存中:
let a = 1
let b = a
它們在內(nèi)存中表現(xiàn)為:

所以說,基本數(shù)據(jù)類型做修改,修改a或者修改b不會對對方造成影響。
我們看下引用數(shù)據(jù)——鍵存在棧內(nèi)存,值存在堆內(nèi)存中:
但是棧內(nèi)存會提供一個引用的地址指向堆內(nèi)存中的值
let a = [0,1,2]
let b = a

當(dāng)發(fā)生 b = a 時,其實只是復(fù)制a的引用地址,而不是里邊的值,如下圖:

這個時候當(dāng)我們修改值的時候,其實也就是修改a和b指向的同一個地址,就是淺拷貝。如下圖:
let a = [0,1,2]
let b = a
b[0] = 1
console.log(a) // [1, 1, 2]
console.log(b) // [1, 1, 2]

所以,重新再堆內(nèi)存中開辟一個新的地址存放b的值,就可以達到深拷貝的效果了:

所以接下來講就是深拷貝的方法了。
3.方法
- Object.assign()
Object.assign(target, ...sources);
target: 目標(biāo)對象
sources: 源對象
// demo
let obj1 = {a: 1, b: 2}
let obj2 = Object.assign({c: 4, d: 5}, obj1)
console.log(obj1); // {a: 1, b: 2}
console.log(obj2); // {c: 4, d: 5, a: 1, b: 2}
- Array.prototype.concat()
var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
let arr = [1, 2, { name: 'A' }]
let arr2 = arr.concat()
arr2[2].name = 'B'
console.log(arr) // [1, 2, { name: 'B' }]
console.log(arr2) // [1, 2, { name: 'B' }]
- Array.prototype.slice()
arr.slice()
arr.slice(begin)
arr.slice(begin, end)
let arr = [1, 2, { name: 'A' }]
let arr2 = arr.slice()
arr2[2].name = 'B'
console.log(arr) // [1, 2, { name: 'B' }]
console.log(arr2) // [1, 2, { name: 'B' }]
注意:以上方法對一維數(shù)組對象進行拷貝,也就是說,只有一層的時候它才是深拷貝
- JSON.parse(JSON.stringify())
用JSON.stringify將對象轉(zhuǎn)成JSON字符串,再用JSON.parse()把字符串解析成對象,一去一來,新的對象產(chǎn)生了,而且對象會開辟新的棧,開辟一個新的地址,實現(xiàn)深拷貝
但是,不可以拷貝 undefined, function, RegExp 等
let obj1 = { a: 1, b: 2, c: 3 }
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
obj2.a = 5;
console.log(obj1.a); // 1
console.log(obj2.a); // 5
- 遞歸
function deepClone(target) {
// 定義一個變量
let result;
// 如果當(dāng)前需要深拷貝的是一個對象的話
if (typeof target === 'object') {
// 如果是一個數(shù)組的話
if (Array.isArray(target)) {
result = []; // 將result賦值為一個數(shù)組,并且執(zhí)行遍歷
for (let i in target) {
// 遞歸克隆數(shù)組中的每一項
result.push(deepClone(target[i]))
}
// 判斷如果當(dāng)前的值是null的話;直接賦值為null
} else if(target===null) {
result = null;
// 判斷如果當(dāng)前的值是一個RegExp對象的話,直接賦值
} else if(target.constructor===RegExp){
result = target;
}else {
// 否則是普通對象,直接for in循環(huán),遞歸賦值對象的所有值
result = {};
for (let i in target) {
result[i] = deepClone(target[i]);
}
}
// 如果不是對象的話,就是基本數(shù)據(jù)類型,那么直接賦值
} else {
result = target;
}
// 返回最終結(jié)果
return result;
}
注:此方法雖然可以拷貝Function, null, undefined 等類型,但是不是很完善,使用要根據(jù)自己邏輯進行更改。
最后,留下贊再走!