竹白記事本,筆記紀錄。
JavaScript 竹白記事本
這部分在 JavaScript 是相當重要的觀念,正確了解這個觀念能避免發生不必要的錯誤。
在 Javascript 中,值的傳遞分為兩種:
在其他程式語言有語法可以決定要「傳值」還是「傳址」,但在 JavaScript 中沒有選擇,基本型別 就是「傳值」行為,物件型別 就是「傳址」行為。
當值的型別為 基本型別,那麼永遠都是藉由值的複製來賦值,所以稱作 傳值。
來看程式碼:
10
的型別為 基本型別;a
與 b
時,是互相比較彼此的值,所以回傳 true
。繼續來看另一段程式碼:
b
指定為 a
時,由於 a
的值是 基本型別;b
得到的是 a
的值而不是 a
的記憶體位址;b
就算改變了,a
也不會受到影響,兩者是獨立的。接下來,我們來看 物件型別 的情況。
你可以觀察到,就算 obj1
和 obj2
的屬性名稱與值都相同,但互相比較的結果卻是 false
。
這是因為每個物件都是獨立存在的實體,兩者的記憶體位置並不相同。在比較 物件型別 時,比較的是記憶體位置,而非值。
繼續來看另一段程式碼:
obj2
透過 obj2 = obj1
的方式賦值;ob1
賦值新的物件時,obj1
會指向新的記憶體位置;obj2
依然保持原物件記憶體位置,這時 obj1
和 obj2
彼此之間就沒有關係了,所以比較的結果為 false
。物件型別 之間的傳遞是記憶體位置指向,所以稱作 傳址。
如果一個 物件型別 作為函式參數傳遞,一樣是傳址行為:
直接更改屬性,會影響原物件。
如果我們將參數重新指向新物件,那麼不影響原物件:
物件型別是傳參考,那我們該如何複製物件呢?
複製物件的方式分為兩類:
常見淺拷貝物件的方式:
Object.assign()
將原本的 obj
內容複製到一個空物件中。...
展開運算子展開物件。常見淺拷貝陣列的方式:
總結,淺拷貝使用 ...
展開運算子就對了,簡單易懂。
以上拷貝方式有一個問題,請考慮下面程式碼:
假如物件內還有物件,就算我們使用 ...
展開運算子複製物件,第二層物件還是指向相同的記憶體位置。
因為我們的複製操作只改變外層容器的地址,但沒動到內層,所以這種複製操作稱為「淺拷貝」。也就是說,當我們的物件或陣列是巢狀或多維的,要多注意。
深拷貝就是完全複製一份,不會有共用記憶體的問題。
常見深拷貝物件方法有:
$.extend()
_.cloneDeep()
先用 JSON.parse()
轉再 JSON 格式,再用 JSON.stringify()
轉回 JS 物件:
但有幾點要注意,當物件轉 JSON 格式時,
undefined
、Symbol
會被忽略;NaN
、Infinity
、-Infinity
會被轉成 null
。所以利用 JSON 方法深拷貝物件,只能用在單純只有資料的物件。
jQuery 是常見的 JS 函式庫之一。
jQuery.extend()
,是用來將兩個或更多物件的內容合併到第一個物件,我們可以用它來複製一個全新的物件。
第一個參數預設為 fasle
(可省略),如果是 false
就是淺拷貝,true
為深拷貝。
Lodash 是熱門 JS 工具函式庫之一。
_.cloneDeep(value)
,能深拷貝一個物件: