介紹
本章是關于ECMAScript面向對象實現的第2篇,第1篇我們討論的是概論和CEMAScript的比較,如果你還沒有讀第1篇,在進行本章之前,我強烈建議你先讀一下第1篇,因為本篇實在太長了(35頁)。
英文原文:http://dmitrysoshnikov.com/ecmascript/chapter-7-2-oop-ecmascript-implementation/
注:由于篇幅太長了,難免出現錯誤,時刻保持修正中。
在概論里,我們延伸到了ECMAScript,現在,當我們知道它OOP實現時,我們再來準確定義一下:
數據類型
雖然ECMAScript是可以動態轉化類型的動態弱類型語言,它還是有數據類型的。也就是說,一個對象要屬于一個實實在在的類型。
標準規范里定義了9種數據類型,但只有6種是在ECMAScript程序里可以直接訪問的,它們是:Undefined、Null、Boolean、String、Number、Object。
另外3種類型只能在實現級別訪問(ECMAScript對象是不能使用這些類型的)并用于規范來解釋一些操作行為、保存中間值。這3種類型是:Reference、List和Completion。
因此,Reference是用來解釋delete、typeof、this這樣的操作符,并且包含一個基對象和一個屬性名稱;List描述的是參數列表的行為(在new表達式和函數調用的時候);Completion是用來解釋行為break、continue、return和throw語句的。
原始值類型
回頭來看6中用于ECMAScript程序的數據類型,前5種是原始值類型,包括Undefined、Null、Boolean、String、Number、Object。
原始值類型例子:
這些值是在底層上直接實現的,他們不是object,所以沒有原型,沒有構造函數。
大叔注:這些原生值和我們平時用的(Boolean、String、Number、Object)雖然名字上相似,但不是同一個東西。所以typeof(true)和typeof(Boolean)結果是不一樣的,因為typeof(Boolean)的結果是function,所以函數Boolean、String、Number是有原型的(下面的讀寫屬性章節也會提到)。
想知道數據是哪種類型用typeof是最好不過了,有個例子需要注意一下,如果用typeof來判斷null的類型,結果是object,為什么呢?因為null的類型是定義為Null的。
規范沒有想象解釋這個,但是Brendan Eich (JavaScript發明人)注意到null相對于undefined大多數都是用于對象出現的地方,例如設置一個對象為空引用。但是有些文檔里有些氣人將之歸結為bug,而且將該bug放在Brendan Eich也參與討論的bug列表里,結果就是任其自然,還是把typeof null的結果設置為object(盡管262-3的標準是定義null的類型是Null,262-5已經將標準修改為null的類型是object了)。
Object類型
接著,Object類型(不要和Object構造函數混淆了,現在只討論抽象類型)是描述 ECMAScript對象的唯一一個數據類型。
Object is an unordered collection of key-value pairs.
對象是一個包含key-value對的無序集合
對象的key值被稱為屬性,屬性是原始值和其他對象的容器。如果屬性的值是函數我們稱它為方法 。
例如:
動態性
正如我們在第17章中指出的,ES中的對象是完全動態的。這意味著,在程序執行的時候我們可以任意地添加,修改或刪除對象的屬性。
例如:
有些屬性不能被修改――(只讀屬性、已刪除屬性或不可配置的屬性)。 我們將稍后在屬性特性里講解。
另外,ES5規范規定,靜態對象不能擴展新的屬性,并且它的屬性頁不能刪除或者修改。他們是所謂的凍結對象,可以通過應用Object.freeze(o)方法得到。
在ES5規范里,也使用Object.preventExtensions(o)方法防止擴展,或者使用Object.defineProperty(o)方法來定義屬性:
內置對象、原生對象及宿主對象
有必要需要注意的是規范還區分了這內置對象、元素對象和宿主對象。
內置對象和元素對象是被ECMAScript規范定義和實現的,兩者之間的差異微不足道。所有ECMAScript實現的對象都是原生對象(其中一些是內置對象、一些在程序執行的時候創建,例如用戶自定義對象)。內置對象是原生對象的一個子集、是在程序開始之前內置到ECMAScript里的(例如,parseInt, Match等)。所有的宿主對象是由宿主環境提供的,通常是瀏覽器,并可能包括如window、alert等。
注意,宿主對象可能是ES自身實現的,完全符合規范的語義。從這點來說,他們能稱為“原生宿主”對象(盡快很理論),不過規范沒有定義“原生宿主”對象的概念。
Boolean,String和Number對象
另外,規范也定義了一些原生的特殊包裝類,這些對象是:
1.布爾對象
2.字符串對象
3.數字對象
這些對象的創建,是通過相應的內置構造器創建,并且包含原生值作為其內部屬性,這些對象可以轉換省原始值,反之亦然。
此外,也有對象是由特殊的內置構造函數創建: Function(函數對象構造器)、Array(數組構造器) RegExp(正則表達式構造器)、Math(數學模塊)、 Date(日期的構造器)等等,這些對象也是Object對象類型的值,他們彼此的區別是由內部屬性管理的,我們在下面討論這些內容。
字面量Literal
對于三個對象的值:對象(object),數組(array)和正則表達式(regular expression),他們分別有簡寫的標示符稱為:對象初始化器、數組初始化器、和正則表達式初始化器:
注意,如果上述三個對象進行重新賦值名稱到新的類型上的話,那隨后的實現語義就是按照新賦值的類型來使用,例如在當前的Rhino和老版本SpiderMonkey 1.7的實現上,會成功以new關鍵字的構造器來創建對象,但有些實現(當前Spider/TraceMonkey)字面量的語義在類型改變以后卻不一定改變。
正則表達式字面量和RegExp對象
注意,下面2個例子在第三版的規范里,正則表達式的語義都是等價的,regexp字面量只在一句里存在,并且再解析階段創建,但RegExp構造器創建的卻是新對象,所以這可能會導致出一些問題,如lastIndex的值在測試的時候結果是錯誤的:
關聯數組
各種文字靜態討論,JavaScript對象(經常是用對象初始化器{}來創建)被稱為哈希表哈希表或其它簡單的稱謂:哈希(Ruby或Perl里的概念), 管理數組(PHP里的概念),詞典 (Python里的概念)等。
只有這樣的術語,主要是因為他們的結構都是相似的,就是使用“鍵-值”對來存儲對象,完全符合“關聯數組 ”或“哈希表 ”理論定義的數據結構。 此外,哈希表抽象數據類型通常是在實現層面使用。
但是,盡管術語上來描述這個概念,但實際上這個是錯誤,從ECMAScript來看:ECMAScript只有一個對象以及類型以及它的子類型,這和“鍵-值”對存儲沒有什么區別,因此在這上面沒有特別的概念。 因為任何對象的內部屬性都可以存儲為鍵-值”對:
此外,由于在ECMAScript中對象可以是空的,所以"hash"的概念在這里也是不正確的: