原型鏈
原型對象也是普通的對象,並且也有可能有自己的原型,如果一個原型對象的原型不爲null的話,我們就稱之爲原型鏈(prototype chain)。原型鏈是一個由對象組成的有限的對象鏈,用於實現繼承和屬性共享。
ECMAScript沒有類的概念。但是,重用[reuse]這個理念沒什麼不同(某些方面,甚至比class-更加靈活),可以由prototype chain原型鏈來實現。這種繼承被稱爲delegation based inheritance-基於繼承的委託,或者更通俗一些,叫做原型繼承。
簡單例子:
var a = {
x: 10,
calculate: function (z) {
return this.x + this.y + z
}
};
var b = {
y: 20,
__proto__: a
};
var c = {
y: 30,
__proto__: a
};
// 調用繼承過來的方法
b.calculate(30); // 60
c.calculate(40); // 80
原理很簡單:如果在對象b中找不到calculate方法(也就是對象b中沒有這個calculate屬性), 那麼就會沿着原型鏈開始找。如果這個calculate方法在b的prototype中沒有找到,那麼就會沿着原型鏈找到a的prototype,一直遍歷完整個原型鏈。記住,一旦找到,就返回第一個找到的屬性或者方法。因此,第一個找到的屬性成爲繼承屬性。如果遍歷完整個原型鏈,仍然沒有找到,那麼就會返回undefined。
注意一點,this這個值在一個繼承機制中,仍然是指向它原本屬於的對象,而不是從原型鏈上找到它時,它所屬於的對象。例如,以上的例子,this.y是從b和c中獲取的,而不是a。當然,你也發現了this.x是從a取的,因爲是通過原型鏈機制找到的。
如果一個對象的prototype沒有顯示的聲明過或定義過,那麼__prototype__的默認值就是object.prototype, 而object.prototype也會有一個__prototype__, 這個就是原型鏈的終點了,被設置爲null。
構造函數
原型鏈通常將會在這樣的情況下使用:對象擁有相同或相似的狀態結構(same or similar state structure) (即相同的屬性集合)與 不同的狀態值(different state values)。在這種情況下,我們可以使用構造函數(Constructor) 在 特定模式(specified pattern) 下創建對象。
除了創建對象,構造函數(constructor) 還做了另一件有用的事情—自動爲創建的新對象設置了原型對象(prototype object) 。原型對象存放於 ConstructorFunction.prototype 屬性中。
// 構造函數
function Foo(y) {
// 構造函數將會以特定模式創建對象:被創建的對象都會有"y"屬性
this.y = y;
// "Foo.prototype"存放了新建對象的原型引用
// 所以我們可以將之用於定義繼承和共享屬性或方法
// 所以,和上例一樣,我們有了如下代碼:
// 繼承屬性"x"
Foo.prototype.x = 10;
// 繼承方法"calculate"
Foo.prototype.calculate = function (z) {
return this.x + this.y + z;
};
// 使用foo模式創建 "b" and "c"
var b = new Foo(20);
var c = new Foo(30);
// 調用繼承的方法
b.calculate(30); // 60
c.calculate(40); // 80
// 讓我們看看是否使用了預期的屬性
console.log(
b.__proto__ === Foo.prototype, // true
c.__proto__ === Foo.prototype, // true
// "Foo.prototype"自動創建了一個特殊的屬性"constructor"
// 指向a的構造函數本身
// 實例"b"和"c"可以通過授權找到它並用以檢測自己的構造函數
b.constructor === Foo, // true
c.constructor === Foo, // true
Foo.prototype.constructor === Foo // true
b.calculate === b.__proto__.calculate, // true
b.__proto__.calculate === Foo.prototype.calculate // true
);