淺談js深拷貝和淺拷貝以及方法

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)為:


let a =1; let b = a

所以說,基本數(shù)據(jù)類型做修改,修改a或者修改b不會對對方造成影響。

我們看下引用數(shù)據(jù)——鍵存在內(nèi)存,值存在內(nèi)存中:
但是棧內(nèi)存會提供一個引用的地址指向堆內(nèi)存中的值

let a = [0,1,2]
let b = a

let a = [0,1,2]


當(dāng)發(fā)生 b = a 時,其實只是復(fù)制a的引用地址,而不是里邊的值,如下圖:
let b = 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]

b[0] = 1


所以,重新再堆內(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ù)自己邏輯進行更改。

最后,留下贊再走!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容