C/C++類比實現JavaScript原型機制
為了更加深入的理解JavaScript原型機制,我們現在用虛擬碼來實現,或者說類比該機制,也許我的理解有所謬誤,如果果真如此,還希望不吝賜教。
一、原型機制設計
在JavaScript中,有兩個祖先一樣的對象:Function.prototype和 Object.prototype。
(1) Object.prototype:這個對象是所有對象的根,它自己沒有爹媽,是女媧造出來的。
(2) Function.prototype:這個對象是所有構造器的根,它事實上也沒爹媽,也是女媧造出來的。只不過其內部維護者Object.prototype的引用。
事實上所有對象都維護者一個{prototype}屬性,這是一個內部的私人屬性,無法通過對象來訪問。不過在firefox等瀏覽器中提供了一個共有屬性__proto__來訪問這個私人屬性。在Js中公有屬性名稱和私人屬性名稱可以相同。但是C/C++中不可以,我們需要做些許改變。而這個女媧分別派出了兩位大使MetaObject 和 MetaConstructor,這是C/C++中的類。他們分別負責產生Object.prototype和Function.prototype,(採用單體模式);
1、Object.prototype (1)通用屬性
1.Constructor:對建立對象的函數(構造器)的引用(指標),這裡為Object,主要是為了Object.prototype也屬於Object類型;設計的需要,實際上不是Object構造的。
2.Prototype:私人的prototype屬性,也就是__proto__的值,這裡為null。
(2)通用方法
1. hasOwnProperty(property):判斷對象是否有某個特定的屬性。必須用字串指定該屬性 (例如,
o.hasOwnProperty(”name”))。
2. isPrototypeOf(object):判斷該對象是否為另一個對象的原型。
3. propertyIsEnumerable(property):判斷給定的屬性是否可以用for…in語句進行枚舉。
4. toString():返回對象的原始字串表示。對於Object類,ECMA-262沒有定義這個值,所以不同的ECMAScriipt實現具有不同的值。
5. valueOf():返回最適合該對象的原值。對於許多類,該方法返回的值都與toString()的返回值相同。
畢竟Js是動態語言,而C/C++是靜態語言,我們實現的只是架構而不涉及到具體細節。
2、Function.prototype (1)通用屬性
1. 0...n屬性:這是arguments取參數時的索引,0——n
2. arguments屬性:儲存存進來所有參數,相當於可變參數參數列表。
3. callee屬性:引用當前函數對象,用於函數遞迴,特別是在匿名函數中特別有用。
4. caller屬性:返回調用某個函數的函數對象。
5.constructor屬性:Function.prototype的構造器是Function.
6.length屬性:當建立一個函數的執行個體時,函數的 length 屬性由指令碼引擎初始化為該函數定義中的參數數目。
7.prototype屬性:內部{Prototype}——也即__proto__,為Object.prototype。
(2)通用方法
1.apply方法:這個方法允許你使用一個函數,就好像該函數是其他某個對象的函數方法一樣。
2.call方法:同apply,只不過參數的傳遞形式不同。
3.toString方法:將對象轉換成字串。
4.valueOf方法:自己查吧。
我們在此只提供介面並不實現。
二、資料結構1.
MetaObject——單體模式產生Object.prototype
MetaObject{public: static constructor = Object; //事實上並非Object所構造,只不過是為了使Object.prototype屬於Object類 static __proto__ = {Prototype};private: static {Prototype} = null; //假設私人共有變數不衝突public: //一堆介面}2.
MetaConstructor——單體模式產生Function.prototype
MetaConstructor{public: static constructor = Function; //實際並非Function所構造,只是為了Function.prototype類型為Function static prototype = undefinded; //之所以未定義是因為它不會當成構造器來使用 static __proto__ = {Prototype};private: static {Prototype} = Object.prototype;public: //一堆介面}3.Function——使用者可使用的函數構造器的基類
Function{public: static constructor = Function; static prototype = Function.prototype; static __proto__ = {Prototype};private: static {Prototype} = Function.prototype;}4.Object——對象基類構造器
Object{public: static constructor = Function; static prototype = Object.prototype; static __proto__ = {Prototype};private: static {Prototype} = Function.prototype;}有個統一的公式:若有個對象obj,構造它的的構造器為Constructor,那麼:
obj.__proto__ = Constructor.prototype;
三、原型鏈如有三個類,也即A,B,C(其實就是構造器——函數對象),如B繼承A,C繼承B,那麼就等價於:
function A(){}; //A.prototype = Object.prototype, A.__proto__ = Function.prototypefunction B(){}; //類推function C(){}; //類推B.protype = new A();C.prototype = new B();var c = new C();c中維護者一個B的執行個體,而這個B的執行個體中又維護者一個A的執行個體的引用。等價於:
function A(){};function B(){};function C(){};var a = new A();B.prototype = a;var b = new B();C.prototype = b;c = new C();也就是c中維護者一個B的執行個體b的引用,而b中維護者一個A的執行個體a的引用。說白了這裡的繼承其實也就是共用對象,而且可以形成共用鏈。畫成圖如下:
(a)沒有繼承之前:
function A(){};function B(){};function C(){};var a = new A();var b = new B();var c = new C();結構
(b)繼承之後:
function A(){};function B(){};function C(){};var a = new A();B.prototype = a;var b = new B();C.prototype = b;c = new C();