圖解Javascript原型鏈

原文網址:http://blog.rainy.im/2015/07/20/prototype-chain-in-js/

本文嘗試闡述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

1. functionFunctionObject{}

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);與之類似有的ObjectStringNumber等,都是Js內置類型實例的構造函數。比較特殊的是Object,它用於生成對象類型,其簡寫形式爲{}函數

var o1 = new Object(); typeof(o1); //=> 'object' var o2 = {}; typeof(o2); //=> 'object' typeof(Object); //=> 'function' 

2. prototype VS __proto__

清楚了上面的概念以後再來看prototypeui

Each function has two properties: length and prototypespa

prototypelength是每個函數類型自帶的兩個屬性,而其它非函數類型並無(開頭的例子已經說明),這一點之因此比較容易被忽略或誤解,是由於全部類型的構造函數自己也是函數,因此它們自帶了prototype屬性:prototype

// Node console.log(Object.prototype); //=> {} console.log(Function.prototype);//=> [Function: Empty] console.log(String.prototype); //=> [String: ''] 

除了prototype以外,Js中的全部對象(undefinednull等特殊狀況除外)都有一個內置的[[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 

上面的代碼示例能夠用下圖解釋:

js __proto__

Person是一個函數類型的變量,所以自帶了prototype屬性,prototype屬性中的constructor又指向Person自己;經過new關鍵字生成的Person類的實例p1,經過__proto__屬性指向了Person的原型。這裏的__proto__只是爲了說明實例p1在內部實現的時候與父類之間存在的關聯(指向父類的原型),在實際操做過程當中實例能夠直接經過.獲取父類原型中的屬性,從而實現了繼承的功能。

3. 原型鏈

清楚了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 

js prototype chain

從上面的例子和圖解能夠看出,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 

參考

  1. JavaScript constructors, prototypes, and the new keyword
  2. Javascript 面向對象編程
  3. Professional JavaScript for Web Developers
相關文章
相關標籤/搜索