中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

javascript中怎么區分淺拷貝和深拷貝并實現深拷貝

發布時間:2021-08-23 14:17:43 來源:億速云 閱讀:157 作者:小新 欄目:web開發

這篇文章將為大家詳細講解有關javascript中怎么區分淺拷貝和深拷貝并實現深拷貝,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

什么是拷貝 ?

一個東西的拷貝看起來像是原來的東西,然而它并不是。同時,當你改變拷貝時,原來的東西可不會發生變化。

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

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

值的存儲方式

原始數據類型

原始數據類型包括:

  • Number 如: 1

  • String 如: 'Hello'

  • Boolean 如:true

  • undefined

  • null

這些類型的值與指定給它們的變量緊密相連,也不會同時與多個變量關聯,這意味著你并不需要擔心在JavaScript  中復制這些原始數據類型時發生意外:復制它們得到的是一個確確實實獨立的副本。

我們來看一個例子:

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

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

javascript中怎么區分淺拷貝和深拷貝并實現深拷貝

復合數據類型—— Object 與數組

技術上看,數組也是 Object 對象,所以它們有著相似的表現。關于這點,后文我們會詳細地介紹。

在這里,拷貝變得耐人尋味了起來:復合類型的值在被實例化時僅會被創建一次。也就是說,如果我們進行復合類型的拷貝,實際上是分配給拷貝一個指向原對象的引用。

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

上面的實例展示了淺拷貝的特征。通常而言,我們并不期望得到這種結果——原變量 a 并不應該受到新變量 b  的影響。當我們訪問原變量時,往往造成出乎意料的錯誤。因為你不清楚錯誤的原因,可能會在造成錯誤后進行一會兒的調試,接著“自暴自棄”了起來。

javascript中怎么區分淺拷貝和深拷貝并實現深拷貝

不用急,讓我們看看一些實現深拷貝的方法。

實現深拷貝的方法

Object

有許多方法可以確實地復制一個對象,其中新的 JavaScript 規范提供了我們一種非常快捷的方式。

展開運算符(Spread operator)

它在 ES2015 中被引入,它太吊了,因為它實在是簡潔方便。它可以把原變量“展開”到一個新的變量中。使用方式如下:

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

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

Object.assign

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

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 對象

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

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

要得到讓對象里的對象得到預期的深拷貝,你必須手動復制所有嵌套對象:

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

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

簡單粗暴的深拷貝方式

如果你不知道對象有多少層嵌套呢?手動遍歷對象并手動復制每個嵌套對象可十分繁瑣。有一種方法能粗暴地拷貝下對象。只需將對象轉換為字符串(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

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

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

  • 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.

建議先不糾結,后文有細說。

數組

拷貝數組和拷貝對象相仿,因為數組本質上也是一種對象。

展開運算符

操作起來和對象一樣:

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

數組方法:map, filter, reduce

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

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

或者在復制時修改所需的元素:

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 方法通常用于返回數組的子集。數組的子集從數組的特定下標開始,也可以自定義結束的位置。使用 array.slice() 或  array.slice(0) 時,可以得到 array 數組的拷貝。

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

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

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

獎勵(BONUS):復制自定義類的實例

當你已是專業的 JavaScript  開發人員,并也要復制自定義構造函數或類時,前面已有提到:你不能簡單地將他們轉為字符串然后解析,否則實例的方法會遺失。Don't panic!可以自己定義一個  Copy 方法來得到一個具有所有原對象值的新對象,看看具體實現:

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

如果要將對象內部的對象也運用深拷貝,你得靈活使用有關深拷貝的新技能。我將為自定義構造函數的拷貝方法添加最終的解決方法,使它更加動態。

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

關于“javascript中怎么區分淺拷貝和深拷貝并實現深拷貝”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临海市| 武平县| 呼伦贝尔市| 姚安县| 都匀市| 凤庆县| 崇仁县| 抚顺县| 保靖县| 阿拉善左旗| 澄城县| 襄城县| 班戈县| 太和县| 新兴县| 金阳县| 无极县| 阜新市| 扶沟县| 蒙山县| 湛江市| 荔浦县| 丰都县| 米易县| 韶关市| 云霄县| 隆尧县| 徐水县| 舟曲县| 开远市| 东明县| 福清市| 沙湾县| 尼玛县| 东阿县| 扎赉特旗| 桃园市| 旺苍县| 卓资县| 汉中市| 扬中市|