溫馨提示×

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

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

javascript中怎么區(qū)分淺拷貝和深拷貝并實(shí)現(xiàn)深拷貝

發(fā)布時(shí)間:2021-08-23 14:17:43 來源:億速云 閱讀:151 作者:小新 欄目:web開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)javascript中怎么區(qū)分淺拷貝和深拷貝并實(shí)現(xiàn)深拷貝,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

什么是拷貝 ?

一個(gè)東西的拷貝看起來像是原來的東西,然而它并不是。同時(shí),當(dāng)你改變拷貝時(shí),原來的東西可不會(huì)發(fā)生變化。

在編程時(shí),我們把值存儲(chǔ)在變量里,拷貝意味著用原變量初始化了一個(gè)新變量。請(qǐng)注意,拷貝具有兩個(gè)不同的概念:深拷貝(deep copying) 與  淺拷貝(shallow copying)。深拷貝意味著新變量的所有值都被復(fù)制且與原變量毫不相關(guān);淺拷貝則表示著新變量的某些(子)值仍然與原變量相關(guān)。

為了更好的理解深拷貝與淺拷貝,我們需要知道,JavaScript 是如何存儲(chǔ)一個(gè)值的。

值的存儲(chǔ)方式

原始數(shù)據(jù)類型

原始數(shù)據(jù)類型包括:

  • Number 如: 1

  • String 如: 'Hello'

  • Boolean 如:true

  • undefined

  • null

這些類型的值與指定給它們的變量緊密相連,也不會(huì)同時(shí)與多個(gè)變量關(guān)聯(lián),這意味著你并不需要擔(dān)心在JavaScript  中復(fù)制這些原始數(shù)據(jù)類型時(shí)發(fā)生意外:復(fù)制它們得到的是一個(gè)確確實(shí)實(shí)獨(dú)立的副本。

我們來看一個(gè)例子:

const a = 5 let b = 6 // 創(chuàng)建 a 的拷貝 console.log(b) // 6 console.log(a) // 5

通過執(zhí)行 b = a ,就可以得到 a 的拷貝。此時(shí),將新值重新指定給 b 時(shí),b 的值會(huì)改變,但 a 的值不會(huì)隨之發(fā)生變化。

javascript中怎么區(qū)分淺拷貝和深拷貝并實(shí)現(xiàn)深拷貝

復(fù)合數(shù)據(jù)類型—— Object 與數(shù)組

技術(shù)上看,數(shù)組也是 Object 對(duì)象,所以它們有著相似的表現(xiàn)。關(guān)于這點(diǎn),后文我們會(huì)詳細(xì)地介紹。

在這里,拷貝變得耐人尋味了起來:復(fù)合類型的值在被實(shí)例化時(shí)僅會(huì)被創(chuàng)建一次。也就是說,如果我們進(jìn)行復(fù)合類型的拷貝,實(shí)際上是分配給拷貝一個(gè)指向原對(duì)象的引用。

const a = {     en: 'Hello',     de: 'Hallo',     es: 'Hola',     pt: 'Olà' } let b = a b.pt = 'Oi' console.log(b.pt) // Oi console.log(a.pt) // Oi

上面的實(shí)例展示了淺拷貝的特征。通常而言,我們并不期望得到這種結(jié)果——原變量 a 并不應(yīng)該受到新變量 b  的影響。當(dāng)我們?cè)L問原變量時(shí),往往造成出乎意料的錯(cuò)誤。因?yàn)槟悴磺宄e(cuò)誤的原因,可能會(huì)在造成錯(cuò)誤后進(jìn)行一會(huì)兒的調(diào)試,接著“自暴自棄”了起來。

javascript中怎么區(qū)分淺拷貝和深拷貝并實(shí)現(xiàn)深拷貝

不用急,讓我們看看一些實(shí)現(xiàn)深拷貝的方法。

實(shí)現(xiàn)深拷貝的方法

Object

有許多方法可以確實(shí)地復(fù)制一個(gè)對(duì)象,其中新的 JavaScript 規(guī)范提供了我們一種非??旖莸姆绞?。

展開運(yùn)算符(Spread operator)

它在 ES2015 中被引入,它太吊了,因?yàn)樗鼘?shí)在是簡(jiǎn)潔方便。它可以把原變量“展開”到一個(gè)新的變量中。使用方式如下:

const a = {     en: 'Bye',     de: 'Tschüss' } let b = {...a} // 沒錯(cuò)!就這么簡(jiǎn)單 b.de = 'Ciao' console.log(b.de) // Ciao console.log(a.de) // Tschüss

也可以使用它把兩個(gè)對(duì)象合并在一起,例如 const c = {... a,... b}。

Object.assign

這種方法在展開運(yùn)算符出現(xiàn)之前被廣泛采用,基本上與后者相同。但在使用它時(shí)你可得小心,因?yàn)?Object.assign()  方法的第一個(gè)參數(shù)會(huì)被修改然后返回,所以一般我們會(huì)傳給第一個(gè)參數(shù)一個(gè)空對(duì)象,防止被意外修改。然后,傳你想復(fù)制的對(duì)象給第二個(gè)參數(shù)。

const a = {     en: 'Bye',     de: 'Tschüss' } let b = Object.assign({}, a) b.de = 'Ciao' console.log(b.de) // Ciao console.log(a.de) // Tschüss

陷阱:嵌套的 Object 對(duì)象

在復(fù)制一個(gè)對(duì)象時(shí)有個(gè)很大的陷阱,你也許也發(fā)現(xiàn)了,這個(gè)陷阱存在于上述的兩種拷貝方法:當(dāng)你有一個(gè)嵌套的對(duì)象(數(shù)組)并試圖深拷貝它們時(shí)。該對(duì)象內(nèi)部的對(duì)象并不會(huì)以同樣的方式被拷貝下來——它們會(huì)被淺拷貝。因此,如果你更改得到的拷貝里的對(duì)象,原對(duì)象里的對(duì)象也將改變。下面是此錯(cuò)誤的示例:

const a = {     foods: {       dinner: 'Pasta'     } } let b = {...a} b.foods.dinner = 'Soup' // dinner 并未被深拷貝 console.log(b.foods.dinner) // Soup console.log(a.foods.dinner) // Soup

要得到讓對(duì)象里的對(duì)象得到預(yù)期的深拷貝,你必須手動(dòng)復(fù)制所有嵌套對(duì)象:

const a = {     foods: {       dinner: 'Pasta'     } } let b = {foods: {...a.foods}} b.foods.dinner = 'Soup' console.log(b.foods.dinner) // Soup console.log(a.foods.dinner) // Pasta

如果要拷貝的對(duì)象里不止一個(gè)對(duì)象( foods),可以再次利用一下展開運(yùn)算符。也就是這樣:const b = {... a,foods:{...  a.foods}}。

簡(jiǎn)單粗暴的深拷貝方式

如果你不知道對(duì)象有多少層嵌套呢?手動(dòng)遍歷對(duì)象并手動(dòng)復(fù)制每個(gè)嵌套對(duì)象可十分繁瑣。有一種方法能粗暴地拷貝下對(duì)象。只需將對(duì)象轉(zhuǎn)換為字符串(stringify),然后解析一下(parse)它就完事啦:

const a = {     foods: {       dinner: 'Pasta'     } } let b = JSON.parse(JSON.stringify(a)) b.foods.dinner = 'Soup' console.log(b.foods.dinner) // Soup console.log(a.foods.dinner) // Pasta

如果使用這種方法,你得明白這是無法完全復(fù)制自定義類實(shí)例的。所以只有拷貝僅有 本地JavaScript值(native JavaScript values)  的對(duì)象時(shí)才可以使用此方式。

水平不夠,翻譯不好,放下原文:

  • Here, you have to consider that you will not be able to copy custom class  instances, so you can only use it when you copy objects with native JavaScript  values inside.

建議先不糾結(jié),后文有細(xì)說。

數(shù)組

拷貝數(shù)組和拷貝對(duì)象相仿,因?yàn)閿?shù)組本質(zhì)上也是一種對(duì)象。

展開運(yùn)算符

操作起來和對(duì)象一樣:

const a = [1,2,3] let b = [...a] b[1] = 4 console.log(b[1]) // 4 console.log(a[1]) // 2

數(shù)組方法:map, filter, reduce

運(yùn)用這些方法可以得到一個(gè)新的數(shù)組,里面包含原數(shù)組里的所有值(或部分)。在拷貝過程中還可以修改你想修改的值,上帝啊,這也太方便了吧。

const a = [1,2,3] let b = a.map(el => el) b[1] = 4 console.log(b[1]) // 4 console.log(a[1]) // 2

或者在復(fù)制時(shí)修改所需的元素:

const a = [1,2,3] const b = a.map((el, index) => index === 1 ? 4 : el) console.log(b[1]) // 4 console.log(a[1]) // 2

Array.slice

slice 方法通常用于返回?cái)?shù)組的子集。數(shù)組的子集從數(shù)組的特定下標(biāo)開始,也可以自定義結(jié)束的位置。使用 array.slice() 或  array.slice(0) 時(shí),可以得到 array 數(shù)組的拷貝。

const a = [1,2,3] let b = a.slice(0) b[1] = 4 console.log(b[1]) // 4 console.log(a[1]) // 2

多維數(shù)組(Nested arrays,嵌套數(shù)組)

和 Object  一樣,使用上面的方法并不會(huì)將內(nèi)部元素進(jìn)行同樣的深拷貝。為了防止意外,可以使用JSON.parse(JSON.stringify(someArray))  。

獎(jiǎng)勵(lì)(BONUS):復(fù)制自定義類的實(shí)例

當(dāng)你已是專業(yè)的 JavaScript  開發(fā)人員,并也要復(fù)制自定義構(gòu)造函數(shù)或類時(shí),前面已有提到:你不能簡(jiǎn)單地將他們轉(zhuǎn)為字符串然后解析,否則實(shí)例的方法會(huì)遺失。Don't panic!可以自己定義一個(gè)  Copy 方法來得到一個(gè)具有所有原對(duì)象值的新對(duì)象,看看具體實(shí)現(xiàn):

class Counter {     constructor() {         this.count = 5     }     copy() {         const copy = new Counter()         copy.count = this.count         return copy     } } const originalCounter = new Counter() const copiedCounter = originalCounter.copy() console.log(originalCounter.count) // 5 console.log(copiedCounter.count) // 5 copiedCounter.count = 7 console.log(originalCounter.count) // 5 console.log(copiedCounter.count) // 7

如果要將對(duì)象內(nèi)部的對(duì)象也運(yùn)用深拷貝,你得靈活使用有關(guān)深拷貝的新技能。我將為自定義構(gòu)造函數(shù)的拷貝方法添加最終的解決方法,使它更加動(dòng)態(tài)。

使用此拷貝方法,你可以在構(gòu)造函數(shù)中防止任意數(shù)量地值,而不再需要一一賦值。

關(guān)于“javascript中怎么區(qū)分淺拷貝和深拷貝并實(shí)現(xiàn)深拷貝”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問一下細(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