JS中六種資料型別(六)——Object
阿新 • • 發佈:2019-01-05
ECMAScript中的物件是可變的鍵控集合(即一組資料和功能的集合)。它將很多值聚合在一起,可通過名字訪問這些值。物件也可看做屬性的容器,每個屬性都是一個名/值對。屬性的名字可以是包括空字串在內的任意字串。屬性值可以是除undefined值之外的任何值。物件最常見的用法是建立(create)、設定(set)、查詢(query)、刪除(delete)、檢測(test)和列舉(enumerate)他的屬性。
一、屬性型別 ECMA-262第5版在定義只有內部採用的特性時,描述了屬性的各種特徵。為了表示特性時內部值,該規範把它們放在了兩對方括號中,例如:[[Enumerable]]。ECMAScript物件中有兩種屬性:資料屬性和訪問器屬性。
1.資料屬性
資料屬性包含一個數據值的位置。在這個位置可以讀取和寫入值。資料屬性有4個描述其行為的特性。
a.[[Configurable]]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。這個特性預設值為true。
b.[[Enumerable]]:表示能否通過for-in迴圈返回屬性。這個特性預設值為true。
c.[[Writable]]:表示能否修改屬性的值。這個特性預設值為true。
d.[[Value]]:包含這個屬性的資料值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值儲存在這個位置。這個特性的預設值為undefined。
2.訪問器屬性:
a.[[Configurable]]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為資料屬性。對於直接在物件上定義的屬性,這個特性的預設值為true.
b.[[Enumerable]]:表示能否通過for-in迴圈返回屬性。對於直接在物件上定義的屬性,這個特性的預設值為true。
c.[[Get]]:在讀取屬性時呼叫的函式。預設值為undefined。
d.[[Set]]:在寫入屬性時呼叫的函式。預設值為undefined。
二、建立物件
1.通過new建立物件
new 運算子建立並初始化一個新物件。關鍵字new後跟隨一個函式呼叫。這裡的函式稱做建構函式,建構函式用以初始化一個新建立的物件。JavaScript語言核心中的原始型別都包含內建建構函式。例如:
除了這些內建建構函式,用自定義建構函式來初始化新物件也是非常常見的。 2.物件字面量 建立物件最簡單的方式就是在JavaScript程式碼中使用物件字面量。物件字面量是由若干名/值對組成的對映表,名/值對中間用冒號分隔,名/值對之間用逗號分隔,整個對映表用花括號括起來,結構為:{屬性名1:屬性值1,屬性名2:屬性值2,……}。屬性名可以是JavaScript識別符號也可以是字串字面量(包括空字串)。屬性的值可以是任意型別的JavaScript表示式,表示式的值(可以是原始值也可以是物件值)就是這個屬性的值。請看示例:
3.原型 我們建立的每個物件都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。如果按照字面意思來理解,那麼prototype就是通過呼叫建構函式而建立的那個物件例項的原型物件。使用原型物件的好處是可以讓所有物件例項共享它所包含的屬性和方法。換句話說,不必在建構函式中定義物件例項的資訊,而是可以將這些資訊直接新增到原型物件中,請看示例:
在此,我們將sayName()方法和所有屬性直接新增到了Person的prototype屬性中,建構函式變成了空函式。即使如此,也仍然可以通過呼叫建構函式來建立新物件,而且新物件還會具有相同的屬性和方法。但與建構函式不同的是,新物件的這些屬性和方法是由所有例項共享的。換句話說,person1和person2訪問的都是同一組屬性和同一個sayName()函式。由於物件的原型牽扯到的知識點非常多且複雜,在此就不向大家闡述,我將會在以後的文章中為大家詳細講述原型(prototype)這一概念。除此之外,JavaScript還有一種Object.create()方法來建立物件。 三、基於物件屬性的各種操作 1.物件屬性的獲取 訪問物件屬性可以使用點表示法和方括號表示法。對於點(.)來說,右側必須是一個以屬性名稱命名的簡單識別符號。對於方括號來說([]),方括號內必須是一個計算結果為字串的表示式,這個字串就是屬性的名字。請看示例:
當使用方括號時,我們說方括號內的表示式必須返回字串。其實更嚴謹的說,表示式必須返回字串或返回一個可以轉換為字串的值(這個特點可以用於處理很多情況)。由此我們可以聯想到陣列,這和陣列獲取元素的方法極為相似,其實陣列也是物件,只是其屬性名為陣列下標而已。 2.屬性訪問錯誤 屬性訪問並不總是返回或設定一個值。查詢一個不存在的屬性並不會報錯,如果在物件o自身的屬性或繼承的屬性中均未找到屬性x,屬性訪問表示式o.x返回undefined。回想一下我們person物件有屬性"name",而沒有屬性"subname":
但是如果物件不存在,那麼試圖查詢這個不存在的物件的屬性就會報錯。null和undefined值都沒有屬性,因此查詢這些值的屬性會報錯,請看示例:
除非確定person和person.subname都是(或在行為上)物件,否則不能寫這樣的表示式person.subname.length,因為這樣會報錯,下面提供了兩種避免出錯的方法:
第二種示例使用了 "&&"的一種複合語言習慣的用法,因此只有在person為真值(不能是null或者undefined)的情況下才會計算之後的值,&&的這一行為稱作“短路”。 當然,給null和undefined設定屬性也會報型別錯誤。給其他值設定屬性也不總是成功,有一些屬性時只讀的,不能重新複製,有一些物件不允許新增屬性,但讓人頗感意外的是,這些設定屬性的失敗操作不會報錯:
這是一個歷史遺留問題,這個bug在ECMAScript5的嚴格模式中已經修復。在嚴格模式中,任何失敗的屬性設定操作都會丟擲一個型別錯誤異常。 3.刪除屬性 delete運算子可以刪除物件的屬性。它的運算元應當是一個屬性訪問表示式。讓人感到意外的是,delete只是斷開屬性和宿主物件的聯絡,而不會去操作屬性中的屬性。
delete運算子只能刪除自有屬性,不能刪除繼承屬性(要刪除繼承屬性必須從定義這個屬性的原型物件上刪除它,而且這會影響到所有繼承自這個原型的物件) 當delete表示式刪除成功或沒有任何副作用(比如刪除不存在的屬性)時,它返回true。如果delete後不是一個屬性訪問表示式,delete同樣返回true:
delete不能刪除那些可配置性為false的屬性(儘管可以刪除不可擴充套件的可配置屬性)。某些內建物件的屬性是不可配置的,比如通過變數宣告和函式宣告建立的全域性物件的屬性。在嚴格模式中,刪除一個不可配置屬性會報一個型別錯誤。在非嚴格模式中,這些情況下的delete操作會返回false:
在非嚴格模式中刪除全域性物件的可配置屬性時,可以省略對全域性物件的引用,直接在delete操作符後跟隨要刪除的屬性名即可:
然後再嚴格模式中,delete後跟隨一個非法的運算元(比如x),則會報一個語法錯誤,因此必須顯式指定物件及其屬性:
4.列舉屬性 for-in語句可用來遍歷一個物件中的所有屬性名。該列舉過程將會列出所有的屬性——包括函式和你可能不關心的原型中的屬性——所以有必要過濾掉那些你不想要的值。最為常用的過濾器是hasOwnProperty方法,以及使用typeof來排除函式:
屬性名出現得順序是不確定的,因此要對任何可能出現的順序有所準備。如果你想要確保屬性以特定的順序出現,最好的辦法就是完全避免使用for-in語句,而是建立一個數組,在其中以正確的順序包含屬性名:
通過使用for而不是for-in ,可以得到我們想要的屬性,而不用擔心可能發掘出原型鏈中的屬性,並且我們按正確的順序獲得了它們的值。
一、屬性型別 ECMA-262第5版在定義只有內部採用的特性時,描述了屬性的各種特徵。為了表示特性時內部值,該規範把它們放在了兩對方括號中,例如:[[Enumerable]]。ECMAScript物件中有兩種屬性:資料屬性和訪問器屬性。
除了這些內建建構函式,用自定義建構函式來初始化新物件也是非常常見的。 2.物件字面量 建立物件最簡單的方式就是在JavaScript程式碼中使用物件字面量。物件字面量是由若干名/值對組成的對映表,名/值對中間用冒號分隔,名/值對之間用逗號分隔,整個對映表用花括號括起來,結構為:{屬性名1:屬性值1,屬性名2:屬性值2,……}。屬性名可以是JavaScript識別符號也可以是字串字面量(包括空字串)。屬性的值可以是任意型別的JavaScript表示式,表示式的值(可以是原始值也可以是物件值)就是這個屬性的值。請看示例:
3.原型 我們建立的每個物件都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。如果按照字面意思來理解,那麼prototype就是通過呼叫建構函式而建立的那個物件例項的原型物件。使用原型物件的好處是可以讓所有物件例項共享它所包含的屬性和方法。換句話說,不必在建構函式中定義物件例項的資訊,而是可以將這些資訊直接新增到原型物件中,請看示例:
在此,我們將sayName()方法和所有屬性直接新增到了Person的prototype屬性中,建構函式變成了空函式。即使如此,也仍然可以通過呼叫建構函式來建立新物件,而且新物件還會具有相同的屬性和方法。但與建構函式不同的是,新物件的這些屬性和方法是由所有例項共享的。換句話說,person1和person2訪問的都是同一組屬性和同一個sayName()函式。由於物件的原型牽扯到的知識點非常多且複雜,在此就不向大家闡述,我將會在以後的文章中為大家詳細講述原型(prototype)這一概念。除此之外,JavaScript還有一種Object.create()方法來建立物件。 三、基於物件屬性的各種操作 1.物件屬性的獲取 訪問物件屬性可以使用點表示法和方括號表示法。對於點(.)來說,右側必須是一個以屬性名稱命名的簡單識別符號。對於方括號來說([]),方括號內必須是一個計算結果為字串的表示式,這個字串就是屬性的名字。請看示例:
當使用方括號時,我們說方括號內的表示式必須返回字串。其實更嚴謹的說,表示式必須返回字串或返回一個可以轉換為字串的值(這個特點可以用於處理很多情況)。由此我們可以聯想到陣列,這和陣列獲取元素的方法極為相似,其實陣列也是物件,只是其屬性名為陣列下標而已。 2.屬性訪問錯誤 屬性訪問並不總是返回或設定一個值。查詢一個不存在的屬性並不會報錯,如果在物件o自身的屬性或繼承的屬性中均未找到屬性x,屬性訪問表示式o.x返回undefined。回想一下我們person物件有屬性"name",而沒有屬性"subname":
但是如果物件不存在,那麼試圖查詢這個不存在的物件的屬性就會報錯。null和undefined值都沒有屬性,因此查詢這些值的屬性會報錯,請看示例:
除非確定person和person.subname都是(或在行為上)物件,否則不能寫這樣的表示式person.subname.length,因為這樣會報錯,下面提供了兩種避免出錯的方法:
第二種示例使用了 "&&"的一種複合語言習慣的用法,因此只有在person為真值(不能是null或者undefined)的情況下才會計算之後的值,&&的這一行為稱作“短路”。 當然,給null和undefined設定屬性也會報型別錯誤。給其他值設定屬性也不總是成功,有一些屬性時只讀的,不能重新複製,有一些物件不允許新增屬性,但讓人頗感意外的是,這些設定屬性的失敗操作不會報錯:
這是一個歷史遺留問題,這個bug在ECMAScript5的嚴格模式中已經修復。在嚴格模式中,任何失敗的屬性設定操作都會丟擲一個型別錯誤異常。 3.刪除屬性 delete運算子可以刪除物件的屬性。它的運算元應當是一個屬性訪問表示式。讓人感到意外的是,delete只是斷開屬性和宿主物件的聯絡,而不會去操作屬性中的屬性。
delete運算子只能刪除自有屬性,不能刪除繼承屬性(要刪除繼承屬性必須從定義這個屬性的原型物件上刪除它,而且這會影響到所有繼承自這個原型的物件) 當delete表示式刪除成功或沒有任何副作用(比如刪除不存在的屬性)時,它返回true。如果delete後不是一個屬性訪問表示式,delete同樣返回true:
delete不能刪除那些可配置性為false的屬性(儘管可以刪除不可擴充套件的可配置屬性)。某些內建物件的屬性是不可配置的,比如通過變數宣告和函式宣告建立的全域性物件的屬性。在嚴格模式中,刪除一個不可配置屬性會報一個型別錯誤。在非嚴格模式中,這些情況下的delete操作會返回false:
在非嚴格模式中刪除全域性物件的可配置屬性時,可以省略對全域性物件的引用,直接在delete操作符後跟隨要刪除的屬性名即可:
然後再嚴格模式中,delete後跟隨一個非法的運算元(比如x),則會報一個語法錯誤,因此必須顯式指定物件及其屬性:
4.列舉屬性 for-in語句可用來遍歷一個物件中的所有屬性名。該列舉過程將會列出所有的屬性——包括函式和你可能不關心的原型中的屬性——所以有必要過濾掉那些你不想要的值。最為常用的過濾器是hasOwnProperty方法,以及使用typeof來排除函式:
屬性名出現得順序是不確定的,因此要對任何可能出現的順序有所準備。如果你想要確保屬性以特定的順序出現,最好的辦法就是完全避免使用for-in語句,而是建立一個數組,在其中以正確的順序包含屬性名:
通過使用for而不是for-in ,可以得到我們想要的屬性,而不用擔心可能發掘出原型鏈中的屬性,並且我們按正確的順序獲得了它們的值。