溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript賦值,淺復(fù)制和深復(fù)制的區(qū)別是什么

發(fā)布時(shí)間:2022-05-26 09:11:07 來(lái)源:億速云 閱讀:118 作者:zzz 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“JavaScript賦值,淺復(fù)制和深復(fù)制的區(qū)別是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

1、變量賦值

不知道會(huì)不會(huì)有人會(huì)和我一樣,會(huì)覺(jué)得淺復(fù)制就是通過(guò)=操作符將一個(gè)變量賦值給另外一個(gè)變量。但實(shí)際上,淺復(fù)制和變量賦值之間是存在區(qū)別的。所以,我們先來(lái)了解一下變量賦值。

1.1 原始值和引用值

原始值(primitive value):最簡(jiǎn)單的數(shù)據(jù),即:Undefined、Null、Boolean、Number、String、BigInt、Symbol這其中類型的值。保存原始值的變量是按值訪問(wèn)的,我們操作的就是存儲(chǔ)在變量中的實(shí)際值。

引用值(reference value):有多個(gè)值構(gòu)成的對(duì)象,即 Object 類型。引用值是保存在內(nèi)存中的對(duì)象,但是 JavaScript 不允許直接訪問(wèn)內(nèi)存位置,所以不能直接操作對(duì)象所在的內(nèi)存空間。在操作對(duì)象時(shí),實(shí)際操作的是該對(duì)象的引用(reference) 而非實(shí)際的對(duì)象本身。保存引用值得變量實(shí)際上存儲(chǔ)得是對(duì)象得引用,是按引用訪問(wèn)得。

1.2 賦值

首先說(shuō)明這里說(shuō)的賦值,不是直接把引用值(例如:{})或者原始值(例如:false、1、"str"等)直接賦值給一個(gè)變量。而是通過(guò)變量把一個(gè)值賦值給另一個(gè)變量。

原始值賦值:保存原始值的變量是按值訪問(wèn)的,所以通過(guò)變量把一個(gè)原始值賦值給另一個(gè)變量時(shí),原始值會(huì)被復(fù)制到新變量的位置。

let num1 = 5;
let num2 = num1;
console.log(num1, num2);  // 5 5
num2 = 4;
console.log(num1, num2);  // 5 4

可以看出 num2 通過(guò) num1 被賦值為5,保存的是同一個(gè)原始值。而且兩個(gè)變量相互獨(dú)立,互不干擾。

具體賦值過(guò)程如下:

JavaScript賦值,淺復(fù)制和深復(fù)制的區(qū)別是什么

引用值賦值:保存引用值的變量是按引用訪問(wèn)的,通過(guò)變量把一個(gè)引用賦值給另一個(gè)變量時(shí),存儲(chǔ)在變量中的值也會(huì)被復(fù)制到新變量的位置。但是,這里復(fù)制的實(shí)際上是一個(gè)指向存儲(chǔ)在堆內(nèi)存中對(duì)象的指針。賦值后,兩個(gè)變量實(shí)際上指向同一個(gè)對(duì)象。所以兩個(gè)變量通過(guò)引用對(duì)對(duì)象的操作會(huì)互相影響。

let obj1 = {};
let obj2 = obj1;
console.log(obj1);  // {}
console.log(obj2);  // {}
obj1.name = 'haha';
console.log(obj1);  // { name: 'haha' }
console.log(obj2);  // { name: 'haha' }
obj1.age = 24;
console.log(obj1);  // { name: 'haha', age: 24 }
console.log(obj2);  // { name: 'haha', age: 24 }

如上代碼,通過(guò) obj1 將指向?qū)ο蟮囊觅x值給 obj2后, obj1 和 obj2 保存了指向同一對(duì)象的引用,所以操作的是同一對(duì)象。

具體可見(jiàn)下圖:

JavaScript賦值,淺復(fù)制和深復(fù)制的區(qū)別是什么

接下來(lái)要說(shuō)的淺復(fù)制和深復(fù)制就是針對(duì)引用值而言的。

二、淺復(fù)制(Shallow Copy)

我們先來(lái)看一篇博客中對(duì)于淺復(fù)制的定義:

An object is said to be shallow copied when the source top-level properties are copied without any reference and there exist a source property whose value is an object and is copied as a reference. If the source value is a reference to an object, it only copies that reference value to the target object.

對(duì)此,個(gè)人的理解淺復(fù)制就是復(fù)制該對(duì)象的的每個(gè)屬性,如果該屬性值是原始值,則復(fù)制該原始值,如果屬性值是一個(gè)對(duì)象,那么就復(fù)制該對(duì)象的引用。

即:淺復(fù)制將復(fù)制頂層屬性,但嵌套對(duì)象在原始(源)和拷貝(目標(biāo))之間共享

2.1 原生 JavaScript 中的淺復(fù)制

Object.assign()

Object.assign()  方法將所有可枚舉(Object.propertyIsEnumerable() 返回 true)和自有(Object.hasOwnProperty() 返回 true)屬性從一個(gè)或多個(gè)源對(duì)象復(fù)制(淺復(fù)制) 到目標(biāo)對(duì)象,返回修改后的對(duì)象。

如下代碼可以看出,淺復(fù)制和變量賦值不同,修改對(duì)象的屬性值互不影響。

const source = { a: 1, b: 2 };
const objCopied = Object.assign({}, source);

console.log(objCopied); // { a: 1, b: 2 }

source.a = 3;
console.log(source);  // { a: 3, b: 2 }
console.log(objCopied);  // { a: 1, b: 2 }

objCopied.a = 4;
console.log(source);  // { a: 3, b: 2 }
console.log(objCopied);  // { a: 4, b: 2 }

對(duì)象內(nèi)的嵌套對(duì)象在源對(duì)象和拷貝對(duì)象之間還是共享的,如上代碼,修改對(duì)象內(nèi)對(duì)象的屬性時(shí)會(huì)相互影響。

const source = { a : {b : 1} };
const objCopied = Object.assign({}, source);

console.log(objCopied); // { a: { b: 1 } }

source.a.b = 3;
console.log(source);  // { a: { b: 3 } }
console.log(objCopied);  // { a: { b: 3 } }

但是注意如下代碼中,source.a = {};修改的是源對(duì)象中屬性的值,這個(gè)并不共享。

const source = { a : {b : 1} };
const objCopied = Object.assign({}, source);

console.log(objCopied); // { a: { b: 1 } }

source.a = {};
console.log(source);  // { a: {} }
console.log(objCopied);  // { a: { b: 1 } }

展開(kāi)運(yùn)算符(...):展開(kāi)語(yǔ)法(Spread syntax), 可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時(shí), 將數(shù)組表達(dá)式或者string在語(yǔ)法層面展開(kāi);還可以在構(gòu)造字面量對(duì)象時(shí), 將對(duì)象表達(dá)式按key-value的方式展開(kāi)。

const source = { a : {b : 1}, c: 2 };
const objCopied = {...source}

console.log(objCopied); // { a: { b: 1 }, c: 2 }

source.c = 3;
console.log(source);  // { a: { b: 1 }, c: 3 }
console.log(objCopied);  // { a: { b: 1 }, c: 2 }

source.a.b = 3;
console.log(source);  // { a: { b: 3 }, c: 3 }
console.log(objCopied);  // { a: { b: 3 }, c: 2 }

2.2 淺復(fù)制的手動(dòng)實(shí)現(xiàn)

function shallowClone(source) {
    // 如果是原始值,直接返回
    if (typeof source !== 'object') {
        return source;
    }

    // 拷貝后的對(duì)象
    const copied = Array.isArray(source) ? [] : {};
    
    // 遍歷對(duì)象的key
    for(let key in source) {
        // 如果key是對(duì)象的自有屬性
        if(source.hasOwnProperty(key)) {
            // 復(fù)制屬性
            copied[key] = source[key]
        }
    }
    
    // 返回拷貝后的對(duì)象
    return copied;
}

三、深復(fù)制(Deep Copy)

首先來(lái)看深復(fù)制的定義:

A deep copy will duplicate every object it encounters. The copy and the original object will not share anything, so it will be a copy of the original.

與淺復(fù)制不同時(shí),當(dāng)源對(duì)象屬性的值為對(duì)象時(shí),賦值的是該對(duì)象,而不是對(duì)象的引用。所以深復(fù)制中,源對(duì)象和拷貝對(duì)象之間不存在任何共享的內(nèi)容。

2.1 原生 JavaScript 中的深復(fù)制

JSON.parse(JSON.stringify(object))

JavaScript 中最常見(jiàn)的深復(fù)制的方法就是JSON.parse(JSON.stringify(object))

如下代碼所示,深復(fù)制中源對(duì)象和拷貝對(duì)象不共享任何內(nèi)容,即使是嵌套對(duì)象。

let obj = { 
    a: 1,
    b: { 
        c: 2,
    },
}
let newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }

2.2 深復(fù)制的手動(dòng)實(shí)現(xiàn)

function deepClone(source) {
    // 如果是原始值,直接返回
    if (typeof source !== 'object') {
        return source;
    }
    // 拷貝后的對(duì)象
    const copied = Array.isArray(source) ? [] : {};
    // 遍歷對(duì)象的key
    for(let key in source) {
        // 如果key是對(duì)象的自有屬性
        if(source.hasOwnProperty(key)) {
            // 深復(fù)制
            copied[key] = deepClone(source[key]);
        }
    }
    return copied;
}

有關(guān)淺復(fù)制和深復(fù)制的手動(dòng)實(shí)現(xiàn),這里只是簡(jiǎn)單實(shí)現(xiàn)了一下。其中還有很多細(xì)節(jié)未實(shí)現(xiàn),具體的實(shí)現(xiàn)大家可以參見(jiàn) lodash 中的實(shí)現(xiàn)。

小結(jié)

  • 賦值操作符是把一個(gè)對(duì)象的引用賦值給一個(gè)變量,所以變量中存儲(chǔ)的是對(duì)象的引用

  • 淺復(fù)制是復(fù)制源對(duì)象的每個(gè)屬性,但如果屬性值是對(duì)象,那么復(fù)制的是這個(gè)對(duì)象的引用。所以源對(duì)象和拷貝對(duì)象之間共享嵌套對(duì)象。

  • 深復(fù)制與淺復(fù)制不同的地方在于,如果屬性值為對(duì)象,那么會(huì)復(fù)制該對(duì)象。源對(duì)象和拷貝對(duì)象之間不存在共享的內(nèi)容。

“JavaScript賦值,淺復(fù)制和深復(fù)制的區(qū)別是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI