JavaScript中幾乎所有東西都是一個對象,除了六種基本類型數據 - null,undefined,strings,numbers,boolean和symbols。
任何不是原始值的東西都是Object。這包括數組,函數,構造函數和對象本身。
對象
從概念上講,對象在所有編程語言中都是相同的。它們使用具有屬性和方法的代碼來表示真實世界。
例如,如果您的對象是學生,則它將具有名稱,年齡,地址,ID等屬性以及updateAddress,updateName等方法。
在JavaScript中,將對象視爲包含元素項的列表,並且列表中的每個項(屬性或方法)都由內存中的鍵值對存儲。
讓我們看一個對象的例子。
firstObj 是一個對象,有2個屬性:1,age;value 爲 foo 和 28。
JavaScript對象在創建方式上有所不同。不需要非得用class創建,並且可以使用字面量表示法聲明。
對象創建
我們可以在JavaScript中以多種方式創建對象,讓我們來看看都有哪些。
1. 對象字面量(最直接的方式)。對象字面量是用大括號括起來的以逗號分隔的鍵值對列表。對象字面量屬性值可以是任何數據類型,包括數組文字,函數,嵌套對象字面量或基本數據類型。
注意:上面的學生對象鍵可以通過點表示法訪問,即student.id,student.name或通過方括號表示法,即學生['id'],學生['姓名']等
2. Object.create()。該方法使用指定的原型和舊對象的屬性創建一個新對象。
注意:默認情況下,每個JavaScript函數都有一個原型對象屬性(默認情況下它是空的)。方法或屬性可以附加到此屬性。
下面是對象__proto__的輸出:
我們現在可以使用Object.create()方法向newStudent對象添加新屬性和數據。
注意:newStudent能夠訪問student對象的鍵和值,因爲它已被添加到newStudent的原型鏈中,這是我們在javascript中繼承的一種方式。也就是說,newStudent將存儲一個指向student對象的鏈接。讀取屬性時也會查詢此父對象。
父對象可以有父對象,依此類推。重複這一過程,直到我們到達一個沒有任何父項的對象,即父項爲空。
3. 對象實例。將Object constructor與“new”關鍵字結合使用可以讓我們初始化新對象。
我們來看一個例子吧。
但是,new Object() 不適合需要創建同一類型的多個對象的情況,因爲它需要爲每個這樣的對象重複編寫上面的代碼。
爲了解決這個問題,我們可以使用下一個方法。
4. 對象構造器。當我們需要一種可以多次創建對象“類型”的方法時,構造函數非常有用,而無需每次都重新定義對象,這可以使用Object Constructor函數來實現。
我們來看一個例子吧。
我們創建了兩個具有相同屬性但具有不同值的對象。
5. Object.assign()。這是從其他對象創建新對象的另一種方法。
它將所有可枚舉的自有屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。讓我們通過一個例子來理解:
Object.assign() 有很多用例,比如對象克隆,合併對象等。
6. Object.fromEntries()。方法將鍵值對列表轉換爲對象。我們來看一個例子吧
注意:創建對象的最佳方法是通過字面量表示法,因爲它在源代碼中佔用的空間更少。它可以清楚地識別出發生了什麼,所以使用new Object(),你實際上只是輸入更多(理論上,如果沒有被JavaScript引擎優化)和進行不必要的函數調用。此外,字面量表示法創建對象,並在同一行代碼中分配屬性,而其他代碼則不然。
如何添加/更新和刪除對象的屬性
如前所述,可以通過點 或 括號表示法添加對象的屬性。讓我們看一個例子
這裏,name 和 city 是對象屬性。
對象只能包含一個且具有一個值的鍵,也就是說同一個鍵只能有一個值。
屬性名稱可以是字符串,數字或特殊字符,也可以是動態屬性,但如果屬性名稱不是字符串,則必須使用括號表示法訪問它。因此,如果我們需要訪問上面示例中的屬性1,我們可以執行a[1],但是a.1將返回語法錯誤。而a.name或[“name”]則都可以。
要更新屬性,我們可以再次使用上述兩種表示法。如果我們爲已創建的屬性添加值,則會更新這個屬性的值。
我們還可以通過Object函數方法( 如Object.defineProperties() 或 Object.defineProperty() )創建和更新對象的屬性。
要刪除對象的屬性,我們可以使用delete關鍵字,來執行此操作。
如果成功刪除屬性,則返回值delete爲true。否則,它將是錯誤的。
如何迭代對象屬性?
如果我們想要訪問所有對象鍵值對的情況下,會出現這種需求。
使用循環 - for in 和 for of
在 for in 的情況下,它迭代一個對象並逐個返回屬性。
Key將逐個對應對象的屬性,[key]返回該值。對於for in循環也迭代原型鏈並返回父鍵,所以如果你看到更多的鍵,不要感到驚訝。爲了避免看到更多的鍵,我們可以執行hasOwnProperty 檢查以僅獲取當前對象鍵。
在 for of 情況下,它迭代遍歷可迭代對象,僅獲取當前對象的key。這點也是和 for in 的區別。更多詳細解釋,可以參考MDN for…of。
Object函數中有各種方法,它們只會訪問當前對象的屬性和值,而不是其原型鏈。
1. Object.keys() 或 Object.getOwnPropertyNames()。 返回字符串鍵數組。
2. Object.values(). 返回一個值數組。
3. Object.entries(). 返回 [key, value] 爲元素的二維數組
從輸出結果看,上面的屬性順序是不固定的。
如何檢查對象中的屬性是否存在
有三種方法可以檢查對象中是否存在屬性。
1. 使用hasOwnProperty。此方法返回一個布爾值,表示對象本身是否具有指定的屬性,而不是父/繼承屬性。
注意:即使屬性的值爲 null 或 undefined,hasOwnProperty 也會返回true。
如果我們將hasOwnProperty作爲對象中的屬性名稱怎麼辦?這個值得思考。
2. 使用in運算符 - 如果指定的屬性位於指定的對象 或 其原型鏈中(即在其父級內),則 in 運算符返回true。
注意:hasOwnProperty僅檢查當前對象屬性,而 in 運算符中檢查當前+父屬性
3. 使用自定義功能
有多種方式可以通過自定義方法檢查屬性是否存在。其中一個是通過 Object.keys。
什麼是按引用/共享複製和按值複製,它如何應用於對象?
不同之處在於,通過值,我們的意思是每次創建內容時都會執行新的內存分配,而在引用的情況下,我們指向已經創建的內存空間。
在javascript的上下文中,所有原始數據類型都是通過值方法分配的內存,對於一個對象,可以進行值或引用傳遞,根據具體操作情況。
什麼是淺層和深層複製/克隆對象?
淺層和深層副本之間的核心區別在於如何將屬性複製到新對象。
在淺拷貝中,新對象與舊對象共享數據,即在上述示例的情況下使用 = 創建對象的淺拷貝b。因此,在大多數情況下,通過引用傳遞是淺層複製。
此外,淺拷貝將複製頂級屬性,但嵌套對象在原始(源)和副本(目標)之間共享。
淺拷貝的另一種方法是使用Object.assign()。我們來看看這個例子
正如我們在上面看到的 obj.b.c = 30,這是 Object.assign() 的一個陷阱。 Object.assign 只生成淺拷貝。 newObj.b 和 obj.b共享對象的相同引用,沒有製作單獨的副本,而是複製了對象的引用。
在Deep copy中,新對象將擁有自己的一組鍵值對(與原始對象具有相同的值)而不是共享。
讓我們看看做一些深層複製的方法
1. JSON.parse(JSON.stringify(object))
我們無法複製自定義的對象函數,以及鍵對應的值是undefined 或 Symbol的情況,如下:
此外,此方法不適用於循環對象。
注意:循環對象是具有引用自身屬性的對象。
上面將拋出一個錯誤,converting circular structure to JSON.
2. 使用ES6展開運算符
但是,nested對象仍然是淺層複製的。
如何比較兩個對象?
對象的等式 == 和 嚴格相等 === 運算符完全相同,即只有兩個對象的內存引用相同時才相等。
例如,如果兩個變量引用同一個對象,它們是相等的:
未完待續
相關文章:
還可以關注頭條號:「前端知否」