A) 關於prototype chain有幾點說明:
a. 這個鏈的終點是Object的prototype對象:Object.prototype
b. 所有的對象在默認的情況下都有一個原型(__proto__).
c. 原型本身也是對象,所以每個原型自身又有一個原型(__proto__),除了Object.ptototype.
關於c這個觀點可以用一下面的小程序來證明:
- function A(){}
- console.log(A.prototype.__proto__);//Object {}
- console.log(Object.prototype.__proto__);//null
就這上面兩個輸出,下面做詳細的分析,並說明這個proto鏈是怎麼形成的。
1) 先定義一個簡單的函數並創建該函數的對象 var a = new A(); 注意,這裏a,沒有自己的屬性,它的所有的屬性都從原型對象上繼承而來)現在看看內存中這個對象的具體情況
圖 1.1
觀察圖1.1,可以發現a對象的__proto__屬性,這個論證了觀點b的正確性。觀察prototype對象有一個__proto__屬性(綠色方框所示)。這個就說明了觀點b的正確性。
但是,prototype的__proto__所代表的對象是誰呢?查看@37891發現,這個對象是一個Object對象,其實說白了就是Object.prototype所指向的對象。下面給出該對象的內存情況:
圖 1.2
觀察這個對象的constructor,其指向Object的構造器。對比圖1.1可以發現,這個對象是沒有__proto__屬性的,這個對象正是a對象所在的proto鏈條的終點:Object.pototype.
結合上面的說明,可以得到如下圖示:
注意:沿着紅色箭頭組成了proto鏈!!!所以console.log(A.prototype__proto__)爲Object{},而Object.prototype.__proto__爲null.
B) 通過這個prototype chain可以實現屬性的繼承,例程如下:
- function A(x){
- this.x = x ;
- }
- function B(y) {
- this.y = y;
- }
- B.prototype = new A(1);
- function C(z) {
- this.z = z;
- }
- C.prototype = new B(2);
- var c = new C(3);
- var sum = c.x+c.y + c.z;//sum == 6
圖 1.4
先逐步分一下各個對象的內存分析圖:查看一下@14285對象(A的對象)的prototype chain:(留意一下@後面的數字)。
圖 1.5
用圖形表現其prototype chain如下:
圖1.6
通過上圖分析,有如下關係:
- a.__proto__ === A.prototype,
- A.prototype.__proto__===Object.prototype
再看看@14295所代表的對象(B的對象)的prototype chain:
圖1.7
用圖形表現其prototype chain如下:
圖1.8
- b.__proto__ === B.prototype
- B.prototype.__proto__ === A.prototype
- A.prototype.__proto__ === Object.prototype
說明:圖1.8的B.prototype所代表的對象,其實是圖1.6中的對象a,因爲B.prototype的@後面的數字和a對象@後面的數字是一樣的。所以在這裏特地把它們的顏色標識爲綠色,以示爲同一個對象。其餘顏色相同的@+數字,代表着同樣的意思。
所以圖1.8和圖1.7合併起來可以另外表示爲:
圖 1.9
到這裏,B的prototype chain已經和A的prototype chain連接起來了。鏈接起來的關鍵代碼就是B.prototype = new A(1);
同理,再看看對象c的prototype chain:
圖 2.0
用圖形表示爲:
圖 2.2
C.__proto__=== C.prototype
C.prototype. __proto__ == a
注意藍色@14295,可以得出c. __proto__== b,在代碼中體現爲C.prototype = new B(2);
所以圖2.1還可以表示爲:
同時也可以發現,javascript對象屬性搜索的過程是由近到遠的順序,如果c對象中有了y屬性,那麼C.prototype的屬性y是不會訪問到的。
Var c = new (3);
c.y = 4;//
var sum = c.x +c.y ;//==5而不是==3
注意代碼c.y = 4;這是個賦值操作,而不是取值操作;對於賦值操作,javascript總是在原始對象(在這裏是c對象)創建屬性或者對已有的屬性賦值,而不會去修改原型鏈。在javascript中只有查詢屬性(取值操作)時纔會體現到繼承的存在,而設置屬性則和繼承無關。