本文嘗試闡述Js中原型(prototype)、原型鏈(prototype chain)等概念及其做用機制。上一篇文章(圖解Javascript上下文與做用域)介紹了Js中變量做用域的相關概念,實際上關注的一個核心問題是:「在執行當前這行代碼時Js解釋器能夠獲取哪些變量」,而原型與原型鏈實際上仍是關於這一問題。javascript
咱們知道,在Js中一切皆爲對象(Object),可是Js中並無類(class);Js是基於原型(prototype-based)來實現的面向對象(OOP)的編程範式的,但並非全部的對象都擁有prototype
這一屬性:html
var a = {}; console.log(a.prototype); //=> undefined var b = function(){}; console.log(b.prototype); //=> {} var c = 'Hello'; console.log(c.prototype); //=> undefined
prototype
是每一個function
定義時自帶的屬性,可是Js中function
自己也是對象,咱們先來看一下下面幾個概念的差異:java
function
、Function
、Object
和{}
function
是Js的一個關鍵詞,用於定義函數類型的變量,有兩種語法形式:編程
function f1(){ console.log('This is function f1!'); } typeof(f1); //=> 'function' var f2 = function(){ console.log('This is function f2!'); } typeof(f2); //=> 'function'
若是用更加面向對象的方法來定義函數,能夠用Function
:瀏覽器
var f3 = new Function("console.log('This is function f3!');"); f3(); //=> 'This is function f3!' typeof(f3); //=> 'function' typeof(Function); //=> 'function'
實際上Function
就是一個用於構造函數類型變量的類,或者說是函數類型實例的構造函數(constructor);與之類似有的Object
或String
、Number
等,都是Js內置類型實例的構造函數。比較特殊的是Object
,它用於生成對象類型,其簡寫形式爲{}
:函數
var o1 = new Object(); typeof(o1); //=> 'object' var o2 = {}; typeof(o2); //=> 'object' typeof(Object); //=> 'function'
prototype
VS __proto__
清楚了上面的概念以後再來看prototype
:ui
Each function has two properties:
length
andprototype
spa
prototype
和length
是每個函數類型自帶的兩個屬性,而其它非函數類型並無(開頭的例子已經說明),這一點之因此比較容易被忽略或誤解,是由於全部類型的構造函數自己也是函數,因此它們自帶了prototype
屬性:prototype
// Node console.log(Object.prototype); //=> {} console.log(Function.prototype);//=> [Function: Empty] console.log(String.prototype); //=> [String: '']
除了prototype
以外,Js中的全部對象(undefined
、null
等特殊狀況除外)都有一個內置的[[Prototype]]
屬性,指向它「父類」的prototype
,這個內置屬性在ECMA標準中並無給出明確的獲取方式,可是許多Js的實現(如Node、大部分瀏覽器等)都提供了一個__proto__
屬性來指代這一[[Prototype]]
,咱們經過下面的例子來講明實例中的__proto__
是如何指向構造函數的prototype
的:code
var Person = function(){}; Person.prototype.type = 'Person'; Person.prototype.maxAge = 100; var p = new Person(); console.log(p.maxAge); p.name = 'rainy'; Person.prototype.constructor === Person; //=> true p.__proto__ === Person.prototype; //=> true console.log(p.prototype); //=> undefined
上面的代碼示例能夠用下圖解釋:
Person
是一個函數類型的變量,所以自帶了prototype
屬性,prototype
屬性中的constructor
又指向Person
自己;經過new
關鍵字生成的Person
類的實例p1
,經過__proto__
屬性指向了Person
的原型。這裏的__proto__
只是爲了說明實例p1
在內部實現的時候與父類之間存在的關聯(指向父類的原型),在實際操做過程當中實例能夠直接經過.
獲取父類原型中的屬性,從而實現了繼承的功能。
清楚了prototype
與__proto__
的概念與關係以後咱們會對「Js中一切皆爲對象」這句話有更加深入的理解。進而咱們會想到,既然__proto__
是(幾乎)全部對象都內置的屬性,並且指向父類的原型,那是否是意味着咱們能夠「逆流而上」一直找到源頭呢?咱們來看下面的例子:
// Node var Obj = function(){}; var o = new Obj(); o.__proto__ === Obj.prototype; //=> true o.__proto__.constructor === Obj; //=> true Obj.__proto__ === Function.prototype; //=> true Obj.__proto__.constructor === Function; //=> true Function.__proto__ === Function.prototype; //=> true Object.__proto__ === Object.prototype; //=> false Object.__proto__ === Function.prototype; //=> true Function.__proto__.constructor === Function;//=> true Function.__proto__.__proto__; //=> {} Function.__proto__.__proto__ === o.__proto__.__proto__; //=> true o.__proto__.__proto__.__proto__ === null; //=> true
從上面的例子和圖解能夠看出,prototype
對象也有__proto__
屬性,向上追溯一直到null
。
new
關鍵詞的做用就是完成上圖所示實例與父類原型之間關係的串接,並建立一個新的對象;instanceof
關鍵詞的做用也能夠從上圖中看出,實際上就是判斷__proto__
(以及__proto__.__proto__
...)所指向是否父類的原型:
var Obj = function(){}; var o = new Obj(); o instanceof Obj; //=> true o instanceof Object; //=> true o instanceof Function; //=> false o.__proto__ === Obj.prototype; //=> true o.__proto__.__proto__ === Object.prototype; //=> true o.__proto__.__proto__ === Function; //=> false