1. 程式人生 > >淺談 JavaScript 原型鏈

淺談 JavaScript 原型鏈

概述

在 JavaScript 中有種說法叫 “萬物皆物件”,就是說無論是建構函式建立的例項,建構函式本身、原型物件、陣列、函式本質上都是物件,都擁有 __proto__ 屬性,即隱式原型,所有函式都擁有 prototype屬性,即顯式原型(僅限函式),原型物件(prototype 屬性指向的物件),在定義函式時就被建立。


原型鏈指向概述

在 JavaScript 中整個原型鏈及查詢機制用下圖可以完整的表示出來:


JS 原型鏈

原型鏈指向:

1、通過字面量和 new Object() 所建立的物件,他們是建構函式是 function Object()

 的例項,Object建構函式的 prototype 指向原型物件 Object.prototypeObject.prototype 的 constructor 指向建構函式 Object,而例項的 __proto__ 也指向 Object.prototypeObject.prototype 的 __proto__指向 null,所以 Object.prototype 也叫做頂級原型物件。

2、上圖中 new Foo()

 建立的物件是建構函式 function Foo() 的例項,Foo 的 prototype 指向原型物件 Foo.prototypeFoo.prototype 的 constructor 指向建構函式 Foo,而例項的 __proto__ 也指向 Foo.prototype,並且 Foo.prototype 雖然是原型物件,但也是物件,所以是建構函式 Object 的例項,__proto__ 指向頂級原型物件 Object.prototype

3、陣列的建構函式是 function Array() 原型鏈的指向與其他除 Object 以外的建構函式相同,Array.prototype 的 __proto__ 也指向頂級原型物件 Object.prototype,每一個數組都是 Array 的例項,__proto__ 都指向 Array.prototype

4、ObjectArrayFoo 等建構函式的本質也是物件,他們的建構函式是 function Function()Function 的 prototype 指向 Function.prototypeFunction.prototype 的 constructor 指向 Function,所有的建構函式的 __proto__ 都指向 Function.prototype,包括 Function 本身,也就是說建構函式 Function 是由自己構造的,Function.prototype 的 __proto__ 同樣指向頂級原型物件 Object.prototype


prototype 原型物件

prototype 是函式的一個屬性,屬性的值指向了一個物件,所以,只有函式才有 prototype 原型物件。

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, age) {
    this.name = name;
    this.age = age;
}

typeof Person.prototype; // object
Person.prototype.constructor; // Person {}
Person.prototype.job = "qianduan";

var p1 = new Person("panda", 18);
var p2 = new Person("shen", 20);

p1.constructor.prototype; // 例項物件查詢建構函式原型物件的方法

一般會把物件共有的屬性和方法都放在建構函式的原型物件上。


例項、建構函式、原型物件的關係

建構函式的原型 prototype 屬性指向一個原型物件,例項也可以通過 __proto__ 指向原型物件,但本質上例項和建構函式之間是沒有關係的。

1
2
3
4
5
6
7
8
function Person(name, age) {
    this.name = name;
    this.age = age;
}

var p = new Person("nihao", 16);
p.constructor = { name: "haha" };
p.name; // nihao

上面的程式碼中改變了建構函式的值為一個物件,物件中的屬性 name 並沒有影響例項的 name 屬性值。


例項屬性 __proto__

上面訪問例項 p 的原型,實際使用 p.constructor.prototype 去找原型物件,當建構函式的值改變後是找不到原型物件的,所以例項並不是通過 constructor.prototype 去查詢原型物件的,而是通過每一個例項都有的 __proto__ 屬性,這個屬性指向建立例項的建構函式原本的原型物件,這個屬性不是標準,在 IE 下不存在。

1
2
3
4
5
6
7
8
9
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.job = "qianduan";
var p = new Person("nihao", 16);

p.__proto__.job; // qianduan

當建構函式的 prototype 屬性值被改變之後,在之前建立的例項的 __proto__ 屬性值的仍然引用原型物件,所以對建構函式改變前建立的例項是沒有影響的,會影響後面建立的例項。


原型鏈查詢機制

例項物件在呼叫了一個屬性或方法時,如果物件本身沒有這個屬性或方法,會去自己的原型物件查詢,也就是 __proto__ 中查詢,如果原型物件中沒有,去原型物件的原型物件查詢,一般(原型鏈沒有被修改)情況下就是去 __proto__ 的 __proto__ 中查詢,即頂級原型物件 Object.prototype,如果例項物件本身有這個屬性,則直接輸出,不再向上查詢,如果物件本身和原型物件具有同名屬性,則會遮蔽掉原型物件的屬性。

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.job = "qianduan";
var p = new Person("nihao", 16);

p.job; // qianduan
p.job = "houtai";
p.job; // houtai
p.__proto__.job; // qianduan

總結

原型鏈的指向及原型鏈的查詢機制是 JavaScript 中非常重要的基礎知識,理解原型鏈是更深入瞭解繼承和麵向物件程式設計的必經之路。