從零構建JavaScript的對象系統

 1、正統的類與繼承javascript

       類是對象的定義,而對象是類的實例(Instance)。類不可直接使用,要想使用就必須在內存上生成該類的副本,這個副本就是對象。html

       以Java爲例:前端

       public class Group { } // 建立一個類java

       Group a = new Group(); // 實例化一個對象程序員

       經過繼承,子類能夠直接從父類得到其全部的屬性和方法,繼承的實現機制是"複製、拷貝"。瀏覽器

       public class Child extends Parent { } // 建立一個子類,繼承父類的方法dom

 

2、原型與繼承 ide

       和正統的面嚮對象語言不一樣,JavaScript中不存在正統意義的"類"。緣由是Brendan Eich在設計JavaScript的時候,不但願又從新設計一門面向對象的語言(由於已經有C++和Java了),而且他但願把JavaScript設計得更簡單,所以他借鑑了Java的語法,用構造函數代替類,因此JavaScript中的對象是經過構造函數建立的。函數

       這並不奇怪,JavaScript自己就是一個借鑑多種語言,倉促交媾的產物。idea

       嚴格的講,JavaScript中既不存在類,也不存在實例,儘管這些概念是如此的深刻人心,以至於誤用起來是那麼順其天然,但咱們仍是須要明白這一點。

       沒有類,那麼JavaScript怎麼實現繼承呢? Brendan Eich爲構造函數設置了一個prototype屬性。這個屬性包含一個對象(即"原型對象"),全部實例對象須要共享的屬性和方法,都放在這個對象裏面;不須要共享的屬性和方法,就放在構造函數裏面。

       與類繼承的"複製、拷貝"不一樣,原型繼承的機制是"引用、關聯"。JavaScript中全部的對象都是由構造函數生成的,每一個對象都共同繼承構造函數的原型對象中的屬性和方法。

       在對象內部有兩種方式訪問它繼承的原型:

       一、obj.constructor.prototype

       二、obj.__proto__

       第一種方法,源於每一個對象都擁有一個內置屬性constructor指向其構造函數;第二種方法源於每一個對象都有一個內部指針[[prototype]],直接指向其原型,這個內部指針是不可訪問的,可是有些瀏覽器暴露出一個__proto__屬性,用於直接訪問原型對象。

 

3、原型鏈與繼承

       由於全部對象都是由構造函數生成的,也就是說全部對象,都必然繼承某個原型,而原型自己也是對象,它也會繼承別的原型,如此環環相扣就造成了原型鏈。

       JavaScript的對象系統是一個相似族譜的樹狀結構,每個分支都經過原型鏈一脈相承。

   基於原型鏈的繼承機制和做用域鏈的工做機制很是相似:當調用對象的某個屬性時,若是對象的自有屬性中不存在該屬性,那麼JavaScript引擎就會沿着該對象的原型鏈向上查找,直到找到第一個匹配的屬性名爲止。

       原型鏈總有個盡頭吧,就好像DOM只有惟一的根元素同樣,全部原型鏈都匯聚到同一個源頭,那就是Object.prototype,它也是一個對象,也有[[prototype]]屬性,那麼Object.prototype.__proto__ === ?答案是null。

       有人特別擅長髮揮,就根據這個大談「JavaScript原型設計的哲學思想」,什麼道生於無,一輩子二,二生三,三生萬物……這個真的想多了,一個花兩週時間搞出來的「KPI項目」,怎麼也扯不到哲學上去,這就是外行學前端的一個寫照。原型鏈系統中每一個對象都必須關聯到另外一個對象,而且不能循環引用,那麼原型鏈就只能無限延伸下去,這顯然是不可能的,所以只好把個不三不四的 null 拿來做爲原型鏈的終結點,這也在必定程度上解釋了 null 明明不是對象,可是它的數據類型倒是對象。

 

4、原型的關聯規則

       每一個對象的[[prototype]]具體指向誰,或者說每一個對象的原型到底是誰,取決於對象的建立方式。

一、對象直接量的原型對象是Object.prototype;

       var obj = { }; obj.__proto__ === Object.prototype // true

二、經過Object.create( )建立的對象,其原型是由第一個參數指定的對象;

  對象直接量等價於Object.create(Object.prototype)

三、經過構造函數建立的對象,其原型即構造函數的原型對象。

   內置的構造函數的prototype原型是預設好的,咱們能夠修改。內置構造函數的prototype原型並不都是普通的對象,例如:

       typeof Function.prototype // "function"

       Array.isArray( Array.prototype ) // true 

       但這不重要。

   自定義的構造函數,它的原型默認是一個僅含constructor屬性的對象。

   function f () {}; Object.getOwnPropertyNames(f.prototype) // "constructor"

       比較特殊的是函數也有繼承的原型。注意,這裏很容易混淆「函數的繼承原型」與「函數的prototype原型」,它們是兩回事,函數不會從自身的prototype指向的原型對象中繼承任何屬性或方法,全部的函數都共同繼承一個原型對象,那就是Function.prototype。

       Array.__proto__ === Function.prototype // true

       Function.__proto__ === Function.prototype // true

   Object.__proto__ === Function.prototype // true

       var f = function () { }; f.__proto__ === Function.prototype // true

       爲何函數也有__proto__屬性?這不奇怪,由於函數也是對象(可調用的對象)。

 

5、從零構建JavaScript對象系統      

       根據以上規則,咱們試着從零開始構建JavaScript的對象系統。

       首先,創造一個構造函數Object,爲JavaScript空空如也的對象世界播下第一顆種子。而後給它添一些屬性和方法。在Chrome控制檯輸入Object.getOwnPropertyNames(Object),能夠看到Object有二十五個屬性,其中就有prototype。

       prototype幾乎就是JavaScript的繁育後代的生殖系統,因此它很關鍵,咱們須要傳給它一些公共屬性和方法:

   Object.prototype = {/* properties */}

    而後須要給Object定義私有屬性,雖然它是函數,但畢竟函數也是對象,因此可以定義屬性:

       Object.create = function ( ) { }; Object.keys = function ( ) { } ……

   一樣的咱們須要給Function設置原型,可是這個原型不是普通對象,而是函數,所以不能用對象直接量: 

       Function.prototype = {/* properties */} // wrong

       Function.prototype = function () { } // right

       至於爲何這麼作,規範裏只有規定,沒有解釋。

       (參考:http://stackoverflow.com/questions/39698919/why-typeoffunction-prototype-is-function)

       而後將Function.prototype.__proto__ 設置爲 Object.prototype。

       如法炮製,因而咱們有了九大構造函數:Object、Function、Array、String、Number、Boolean、Date、Error、RegExp。

       再來兩個單體內置對象:Math和JSON

       var Math = new Object(); // 這裏採用new「實例化」了一個對象

       Math.random = ……

       Math.max = ……

       ……

 

       如今原型對象之間的關聯有了,函數是否是也效仿着弄一個繼承呢,這樣就省得給每一個函數都定義相同的方法了,簡單粗暴,直接將函數的[[prototype]]都指向Function.prototype完事兒。

         

    JavaScript內置的原型系統基本上就完成了,其它的構造函數和對象就留給程序員自定義設置吧。 

 

參考:阮一峯《JavaScript繼承機制的設計思想》

http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html

相關文章
相關標籤/搜索