在網頁上咱們隨處可見說javascript是一種原型繼承。然而javascript僅僅默認地在使用new操做符的這種特殊狀況下提供了原型繼承。所以,許多js的解讀都讓人感到很迷惑。 這篇文章用於闡述什麼是原型繼承以及如何在js中使用原型繼承。javascript
原型繼承的目的:在 js 中實現一種對象之間的資源(對象屬性)共享。html
關於js的原型繼承,你常會看到以下定義:java
當訪問一個對象的屬性時,js會沿着原型鏈向上遍歷直到尋找到匹配的屬性。編程
大多數js實現使用 proto 屬性來表明原型鏈中的下一層對象。在這篇文章中咱們會看到 proto 和 prototype 究竟有什麼不一樣之處。app
注意: proto 是一種非標準表示方式,你不該該將其用在你的代碼中。在這篇文章中咱們用它來描述js繼承是如何實現的。編程語言
下面代碼顯示了js 引擎如何檢索屬性(讀取)。函數
function getProperty(obj, prop) {
if (obj.hasOwnProperty(prop)) {
return obj[prop]
} else if (obj.__proto__ !== null) {
return getProperty(obj.__proto__, prop)
} else {
return undefined
}
}
複製代碼
讓咱們來看一個經典的案例: 一個2D點元素。一個點有兩個座標 x 和 y 以及一個方法 print。
使用以前咱們所寫的原型繼承方法,咱們將建立一個 Point 對象以及它的三個屬性: x, y, 和 point。要建立一個新的點,咱們只須要使用設置在 Point對象上的 proto 來建立一個新的對象。測試
var Point = {
x: 0,
y: 0,
print: function () { console.log(this.x, this.y); }
};
var p = {x: 10, y: 20, __proto__: Point};
p.print(); // 10 20
複製代碼
讓人迷惑的是每一個教授js原型繼承的人都給了這樣的定義卻沒有給如上的代碼。他們所給的代碼像下面這樣:優化
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
print: function () { console.log(this.x, this.y); }
};
var p = new Point(10, 20);
p.print(); // 10 20
複製代碼
這兩份代碼徹底不一樣。上面的代碼中 Point 是一個函數,咱們添加了一個 prototype 屬性,使用了 new 操做符。這是什麼鬼東西?ui
Brendan Eich 想要將 Javascript 設計成一種傳統的面向對象的編程語言,如同 Java h和 C++ 那樣。在這些語言中,咱們使用 new 操做符來建立一個類的實例。因此他爲 Javascript 添加了 new 操做符。
C++ 有構造函數( constructor )的概念,用於初始化實例屬性。所以,new 操做符必須的做用對象必須是一個函數。
咱們必須把對象的方法放在某一處。因爲咱們使用的是一種原型語言,讓咱們把它置於函數的原型屬性中。
new 操做符建立一個帶參數的 F 函數: new F(arguments...)。經過以下三步來實現:
function New (f) {
① var n = { '__proto__': f.prototype };
return function () {
② f.apply(n, arguments);
③ return n;
};
}
複製代碼
咱們來測試一下它是如何工做的:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
print: function () { console.log(this.x, this.y); }
};
var p1 = new Point(10, 20);
p1.print(); // 10 20
console.log(p1 instanceof Point); // true
var p2 = New (Point)(10, 20);
p2.print(); // 10 20
console.log(p2 instanceof Point); // true
複製代碼
js規範僅爲咱們提供了 new 操做符可使用。Douglas Crockford 找到了一種利用 new 操做符來實現原型繼承的方法!他創造了 Object.create 函數。
Object.create = function (parent) {
function F() {}
F.prototype = parent;
return new F();
};
複製代碼
這種寫法看起來很奇怪但它的工做原理很簡單。它僅僅根據設置在這個函數上的原型來建立了一個新的對象。若是咱們可使用 proto 它的寫法將會是這個樣子:
Object.create = function (parent) {
return { '__proto__': parent };
};
複製代碼
下面的代碼是咱們使用真正的原型繼承來建立的一個點。
var Point = {
x: 0,
y: 0,
print: function () { console.log(this.x, this.y); }
};
var p = Object.create(Point);
p.x = 10;
p.y = 20;
p.print(); // 10 20
複製代碼
咱們看到了究竟什麼是原型繼承而且看到了 js 僅僅是用了一種特別的方法來實現它。
然而,使用原型繼承(Object.create and proto)也存在一些缺點:
若是你可以經過下面的圖理解原型繼承是如何工做的,Congratulations!