又來補強自己的基本觀念了!!沒錯~小女不才,已經入行10個月,這個基本觀念還是沒有確實地建立起來。最近踩了一個大雷,不來好好學習不行了,所以就以這個主題來寫學習筆記了!
好啦!廢話不多說,直接進入正題啦!
物件型別的特性-傳址(by reference)
物件型別是傳址不是傳值,所以跟其他字串、數字不太一樣,在進行複製動作時,並沒有那麼單純。
物件型別因為是傳址,所以其實可以把物件型別用另一種方式去看,就是當宣告一個陣列變數或物件變數時,除了變數的內容,還隱藏著一個東西就是變數內容儲存的位址,也就是物件型別儲存的位址。這部分尤其在進行複製動作的時候,會有更明顯的感受,因為在進行複製時,由於傳址特性,雖然表面上把值複製出來賦值成另一個變數了,但實際上物件型別儲存的位址還是指向同一個,所以這個物件或陣列其實是一樣的東西,直接看一下小小的範例!
來證明一下這兩個物件是不是真的被認定成同個物件!
明明只改動到了objA的name,但是因為他們的資料都被儲存到同個位址,所以其實這樣複製值的話,改objA,objB也會被改動到。
!!所以在看一個物件型別時,除了表面上我們用肉眼看得到的數值(內容)外,更需要去注意背後我們看不到的儲存位址。
而上面這個狀況也就跟這次的重點,也就是深拷貝和淺拷貝有關了!
什麼是淺拷貝?什麼是深拷貝?
所謂的淺拷貝(shallow copy)
「淺拷貝(shallow copy)」就跟字面上的意思一樣,也就是只拷貝到淺層(第一層),也就是前面出現的狀況,只是單純地把值給複製出來,但是後面肉眼看不到的儲存位址還是一樣。在這種情況下,如果被複製的物件型別只要一有變更,複製出來的物件型別也會被改動到。
所謂的深拷貝(deep copy)
「深拷貝(deep copy)」與淺拷貝相反,不只會拷貝到第一層的值,而是會連存儲位址都被複製出來,也就會變成另一個全新的物件或陣列,這時候就算值是相同的,但是因為儲存的位址不同,就會變成不同的物件或陣列。
淺拷貝&深拷貝到底有什麼影響?
接下來透過我自己這陣子的實務經驗來看淺拷貝和深拷貝。
近幾次的實務經驗中,通常拿到物件或陣列,另存一個變數後,除了會拿它本身肉眼可以看得到的值來渲染畫面外,可能還會需要對這個物件或陣列的值進行改動。當被複製,以及複製出來的物件彼此使用到的地方沒有衝突時,單純用淺拷貝的方式進行可能沒有太大的影響(例如:因為某些原因要把objA淺拷貝給objB,主要是使用objB,且不會再使用到objA)。但如果是會需要再次使用objA當作初始值,實際操作則是改動objB的話,以淺拷貝進行複製,就會有很大的影響了(例如:一樣因為某些原因要把objA淺拷貝給objB,畫面操作上會改動到objB,但某些情況下又需要拿objA的值當作初始值,將objB的值重置成objA)。
所以說到底是淺拷貝,還是深拷貝,到底有什麼影響嗎?答案是有,畢竟當我們複製出一份東西出來時,肉眼看名字不一樣了,但其實骨子裡還是一樣的東西,不就跟原本我們要複製的用意互相矛盾了嗎?
該怎麼做到【真。複製】?
當需要【真。複製】的【效果】時候,可以透過以下這幾個方式:
1. 展開運算子「...」
如果物件或陣列很單純,只有一層的話,可以用展開運算子把原本的物件內容解開,放入新的物件,達到真的複製成另一個儲存位址不同的物件。
不過!!!要注意如果物件不單純,有很多層的話,用這個方式,內層的物件或陣列還是無法達到深拷貝的效果,因為解開的物件或陣列只有外面第一層。
這邊可以發現到,objA和objB雖然已經變成不同的物件,但是這兩個物件裡面的陣列卻還是維持不同的陣列,因為只有第一層物件被展開了。
2. Object.assign(target, sources)
這個做法和第一個方式的概念有點類似,簡單來說就是把物件第一層的屬性值複製到另一個物件或陣列裡面,但是更深一層的物件一樣就無法被真正的複製出來。
所以實際上的結果,也會和第一種方式一樣。
3. JSON.parse(JSON.stringify( object ))
這個方法是將目標的物件或陣列,先轉換成Json字串後,再轉換成物件,因為這個物件被重新建立了,也就是一個全新的物件,如此一來就可以達到真正的深拷貝。
除此之外,還可以使用一些函式庫來更優雅地進行深拷貝,例如:lodash。不過基本上,當在處理物件型別時,就是有可能因為沒有注意,而被表面長得一樣的值給蒙蔽了雙眼!其實很多時候,可能並沒有真正地被複製成另一個全新的物件型別。
經過近期的踩雷,才讓我意識到原來我不夠認識物件型別,但也因為這個雷,才讓我得以好好認識這部分的必懂知識,這應該也算是因禍得福(?)。
好啦!這次的筆記差不多就記錄到這裡了,我想自己從今天起應該不會在傻傻地誤觸雷區了XD
就這樣囉!打完收工!
留言列表