1.JavaScript中的對象
JavaScript 中有八種數據類型。有七種原始類型,因為它們的值只包含一種東西(字符串,數字或者其他)。相反,對象則用來存儲鍵值對和更復雜的實體。
對象是具有一些特殊特性的關聯數組。
它們存儲屬性(鍵值對),其中:
- 屬性的鍵必須是字符串或者 symbol(通常是字符串)。
- 值可以是任何類型。
我們可以用下面的方法訪問屬性:
- 點符號:
obj.property。 - 方括號
obj["property"],方括號允許從變量中獲取鍵,例如obj[varWithKey]。
其他操作:
- 刪除屬性:
delete obj.prop。 - 檢查是否存在給定鍵的屬性:
"key" in obj。 - 遍歷對象:
for(let key in obj)循環。
我們在這一章學習的叫做“普通對象(plain object)”,或者就叫對象。
JavaScript 中還有很多其他類型的對象:
Array用于存儲有序數據集合,Date用于存儲時間日期,Error用于存儲錯誤信息。- ……等等。
它們有著各自特別的特性,我們將在后面學習到。有時候大家會說“Array 類型”或“Date 類型”,但其實它們并不是自身所屬的類型,而是屬于一個對象類型即 “object”。它們以不同的方式對 “object” 做了一些擴展。
2.對象引用和復制
賦值了對象的變量存儲的不是對象本身,而是該對象“在內存中的地址” —— 換句話說就是對該對象的“引用”。
當一個對象變量被復制 —— 引用被復制,而該對象自身并沒有被復制。
克隆與合并,Object.assign
克?。嚎梢詣摻ㄒ粋€新對象,通過遍歷已有對象的屬性,并在原始類型值的層面復制它們,以實現對已有對象結構的復制。
let user = { name: "John", age: 30 }; let clone = {}; // 新的空對象 // 將 user 中所有的屬性拷貝到其中 for (let key in user) { clone[key] = user[key]; } // 現在 clone 是帶有相同內容的完全獨立的對象 clone.name = "Pete"; // 改變了其中的數據 alert( user.name ); // 原來的對象中的 name 屬性依然是 John
也可以使用 Object.assign 方法來達成同樣的效果。
語法是:
Object.assign(dest, [src1, src2, src3...])
- 第一個參數
dest是指目標對象。 - 更后面的參數
src1, ..., srcN(可按需傳遞多個參數)是源對象。 - 該方法將所有源對象的屬性拷貝到目標對象
dest中。換句話說,從第二個開始的所有參數的屬性都被拷貝到第一個參數的對象中。 - 調用結果返回
dest。
例如,我們可以用它來合并多個對象:
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// 將 permissions1 和 permissions2 中的所有屬性都拷貝到 user 中
Object.assign(user, permissions1, permissions2);
// 現在 user = { name: "John", canView: true, canEdit: true }
如果被拷貝的屬性的屬性名已經存在,那么它會被覆蓋:
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // 現在 user = { name: "Pete" }
也可以用 Object.assign 代替 for..in 循環來進行簡單克隆:
let user = { name: "John", age: 30 }; let clone = Object.assign({}, user);
它將 user 中的所有屬性拷貝到了一個空對象中,并返回這個新的對象。
還有其他克隆對象的方法,例如使用 spread 語法 clone = {...user}
深層克隆
到現在為止,我們都假設 user 的所有屬性均為原始類型。但屬性可以是對其他對象的引用。
例如:
現在這樣拷貝 clone.sizes = user.sizes 已經不足夠了,因為 user.sizes 是個對象,它會以引用形式被拷貝。因此 clone 和 user 會共用一個 sizes:
為了解決這個問題,并讓 user 和 clone 成為兩個真正獨立的對象,我們應該使用一個拷貝循環來檢查 user[key] 的每個值,如果它是一個對象,那么也復制它的結構。這就是所謂的“深拷貝”。
我們可以使用遞歸來實現它。或者為了不重復造輪子,采用現有的實現,例如 lodash 庫的 _.cloneDeep(obj)。
通過引用對對象進行存儲的一個重要的副作用是聲明為 const 的對象 可以 被修改。
例如:
看起來 (*) 行的代碼會觸發一個錯誤,但實際并沒有。user 的值是一個常量,它必須始終引用同一個對象,但該對象的屬性可以被自由修改。
換句話說,只有當我們嘗試將 user=... 作為一個整體進行賦值時,const user 才會報錯。
也就是說,如果我們真的需要創建常量對象屬性,也是可以的,但使用的是完全不同的方法。
總結
對象通過引用被賦值和拷貝。換句話說,一個變量存儲的不是“對象的值”,而是一個對值的“引用”(內存地址)。因此,拷貝此類變量或將其作為函數參數傳遞時,所拷貝的是引用,而不是對象本身。
所有通過被拷貝的引用的操作(如添加、刪除屬性)都作用在同一個對象上。
為了創建“真正的拷貝”(一個克?。?,我們可以使用 Object.assign 來做所謂的“淺拷貝”(嵌套對象被通過引用進行拷貝)或者使用“深拷貝”函數,例如 _.cloneDeep(obj)。
浙公網安備 33010602011771號