若是想要深刻理解原型鏈,首先須要明確知道什麼時原型對象,什麼是實例對象,以及原型對象,實例對象與構造函數三者之間的關係。
首先,實例對象是指經過關鍵字new,由構造函數構造出的對象實例對象能夠有多個,而且實例對象會繼承它的原型對象上的全部屬性。數組
function Foo() { } var f1=new Foo(); var f2=new Foo();
f1
與f2
就是由構造函數Foo
經過關鍵字new建立的兩個實例對象。
同一個構造函數的實例對象的原型對象是同一個,是構造函數的prototype屬性,在實例對象上能夠經過__proto__屬性獲取。即:函數
console.log(f1.__proto__ === Foo.prototype);//true console.log(f2.__proto__ === Foo.prototype);//true console.log(f1.__proto__ === f2.__proto__);//true
所以當咱們在原型對象上添加屬性或方法時,全部的實例對象都會繼承該屬性或方法,咱們能夠將一些公用的方法定義在Foo.prototype上。spa
每一個實例對象的原型對象上都有一個constructor屬性,這個屬性指向實例對象的構造函數,實例對象能夠從它的原型對象上繼承該屬性。
三者的關係聽起來十分繞口,可是用代碼體現就很清晰了:
實例對象f1
,構造函數Foo
,原型對象Foo.prototype===f1.__proto__
prototype
console.log(Foo.prototype.constructor === Foo);//true console.log(f1.__proto__.constructor === Foo);//true console.log(f1.constructor === Foo);//true console.log(f1.constructor === f1.__proto__.constructor);//true
1.js中規定,全部的對象都有本身的原型對象,原型對象和和函數也是對象,因此他們也有本身的原型對象(__proto__),所以會造成原型鏈。
2.js中讀取屬性和方法的規則:js引擎會首先在對象自己查找屬性和方法,若是找不到就會沿着原型鏈逐層向上查找,最終找不到就會返回undefined。若是找到了就不會再繼續查找,因此當一個實例對象和它的原型對象上具備同名屬性或方法時,只會查找到對象自己的屬性或方法。
使用圖示能夠更好地理解原型鏈:3d
function Foo() { } var f1 = new Foo();
以上這段代碼:
首先有實例對象,構造函數和原型對象
原型對象的constructor
指向構造函數,實例對象繼承的constructor
也是指向構造函數,實例對象和構造函數的__proto__
和prototype
指向同一個原型對象。
而後,由於Foo.prototype
也是一個對象,他也具備自身的原型對象__proto__
,由三者關係咱們能夠知道,Foo.prototype.__proto__.constructor
應該指向Foo.prototype
的構造函數,輸出結果是Object
,即Foo.prototype.__proto__.constructor===Object
,而且Object.prototype
也應該有本身的原型對象,輸出爲null
。
因此有:
再而後,構造函數Foo
和Object
也是實例對象,他們也應該由本身的原型對象__proto__
,而全部的函數都是由構造函數Function
構造的,因此Object.__proto__===Function.prototype
,Foo.__proto__===Function.prototype
,Object.__proto__===Foo.__proto__
,而且Function.prototype
也有本身的原型對象__popto__
,指向了Object.prototype
,能夠畫出:
原型鏈圖示
這就是一條完整的原型鏈了,從圖中咱們能夠發現:Object.prototype
是原型鏈的盡頭,再向上就是null
了,null
是無心義的,一次全部的對象都繼承了Object.prototype
上的屬性和方法。code
經過上面的分析,咱們知道,實例對象共用的屬性和方法都應該定義在了他們的原型對象上,因此數組的屬性和方法都在Array.prototype
上,
實例對象f1,f2
共用的屬性和方法來自Foo.prototype
,因此咱們能夠:Foo.prototype=Array.prototype
這樣f1,f2
就繼承了數組的屬性和方法,可是咱們須要注意一點,此時輸出f1.constructor
,來觀察f1,f2
的構造函數會發現它們已經成了Array
,
這是由於constructor
屬性一樣是從原型對象Foo.prototype
上繼承而來的,因此當Foo.prototype
上的constructor
屬性發生變化時,實例對象f1
的constructor
也會變化。因此咱們若是不想讓構造函數發生變化的話,須要重寫一次Foo.prototype
的constructor
屬性,Foo.prototype=Array.prototype
Foo.prototype.constructor=Foo
。對象
因此咱們須要注意,在修改實例對象的原型對象以後,也要記得修改constructor
屬性blog