在類繼承的語言中,好比 Java ,使用了類來描述實例對象的行爲。JavaScript 中沒有類,因此也沒有使用類繼承。採用的是原型繼承的方式。javascript
原型繼承使用對象來描述實例對象的行爲,這個描述行爲的對象就是原型對象(prototype)。java
prototype 是全部函數都具備的屬性。當一個函數被做爲構造函數生成一個實例對象時,prototype 就是這個實例對象的原型對象。瀏覽器
constructor 表示生成該實例對象的構造函數。 可是實例對象是不存在 constructor 屬性的,這個屬性被保存在了原型對象中。緩存
來看實例:bash
function Foo(){}
var foo1 = new Foo()
console.log(foo1)
console.log(Foo.prototype)
console.log(foo1.constructor === Foo.prototype.constructor)
複製代碼
結果以下: 函數
咱們能夠看到 foo1 對象中是不包含 constructor 屬性的,而 Foo.prototype 中存在 constructor 屬性。可是 foo1 的 constructor 值和 Foo.prototype 的 constructor 值倒是相等的。說明 foo1 的 constructor 屬性實際上是從 Foo.prototype 繼承過來的。這種將屬性不保存在自身,卻能經過自身訪問獲得的設計被稱爲行爲委託。post
在上圖中,咱們看到在 foo1 對象和 Foo.prototype 對象中都有一個屬性 __proto__ 。ui
那麼 __proto__ 是什麼呢? 在繼承中,咱們須要一種向上查找的能力去維持繼承關係。對於 JavaScript 來講就是實例對象查找實例原型,子原型對象查找父原型對象,父原型對象繼續向上查找直到根原型對象,也就是 Object.prototype,Object.prototype向上查找會獲得一個 null 值,指示查找結束。整個查詢路徑構成了原型鏈。this
在瀏覽器的實現中,使用了 __proto__ 屬性來緩存原型對象,全部的對象都擁有這個屬性。這樣對象經過查詢 __proto__ 屬性便能實現向上查找。spa
來看實例:
function Foo(){}
var foo1 = new Foo()
console.log(foo1.__proto__ === Foo.prototype) // true
console.log(Foo.prototype.__proto__ === Object.prototype) // true
複製代碼
實例對象 foo1 的 __proto__ 屬性緩存着原型對象 Foo.prototype 。子原型對象 Foo.prototype 的 __proto__ 屬性緩存着父原型對象 Object.prototype。
從 constructor、prototype、__proto__ 這三個角度去思考,咱們便能很快的畫出整個圖示。
先來思考 constructor ,原型對象的構造函數表示生成實例對象的構造函數,由此得出下圖:
再來思考 prototype,構造函數的 prototype 就是原型對象。補充得出下圖:
最後來思考 __proto__,實例對象的 __proto__屬性緩存着原型對象,原型對象的 __proto__緩存着父原型對象。 實例對象是由構造函數使用 new 操做符生成的。補充得出下圖:
instanceof 運算符用來檢測一個對象的原型鏈中是否存在指定構造函數的原型對象。
用法以下:
object instanceof constructor
複製代碼
來看實例:
function Foo(){}
var foo1 = new Foo()
console.log(foo1 instanceof Foo) // true
console.log(foo1 instanceof Object) // true
複製代碼
經過圖示,咱們能夠清楚地看到 Foo.prototype 和 Object.prototype 都位於原型鏈中。
ECMAScript 5 提供的 Object.getPrototypeOf 能夠用來查看實例對象的原型。
function Foo(){}
var foo1 = new Foo()
console.log(Object.getPrototypeOf(foo1 ) === Foo.prototype) // true
console.log(Object.getPrototypeOf(foo1 ) === Object.prototype) //false
複製代碼
關於內建對象:
關於內建函數:
理解上面的兩個要點,就能理解下面的例子了
Function.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
複製代碼
Function 是內建函數是由 Function() 建立而來,因此它的 __proto__ 屬性中緩存着 Function.prototype。同理可得Object.__proto__ === Function.prototype
。
Function.__proto__.__proto__ === Object.prototype // true
Object.__proto__.__proto__ === Object.prototype // true
Object.__proto__.__proto__.__proto__ === null // true
複製代碼
Function.__proto__是一個內建原型對象,是由 Object() 建立而來的。因此它的 __proto__屬性中緩存着 Object.prototype。同理可得 Object.__proto__.__proto__ === Object.prototype
。 既然 Object.__proto__.__proto__ === Object.prototype
,而 Object.prototype.__proto__ === null ,因此Object.__proto__.__proto__.__proto__ === null