一. 無中生有函數
起初,什麼都沒有。this
造物主說:沒有東西自己也是一種東西啊,因而就有了null:prototype
如今咱們要造點兒東西出來。可是沒有原料怎麼辦?對象
有一個聲音說:不是有null嘛?blog
另外一個聲音說:但是null表明無啊。繼承
造物主說:那就無中生有吧!隊列
因而:ip
JavaScript中的1號對象產生了,咱們把它叫作No. 1。原型
這個No. 1對象可不得了,它是真正的萬物之祖。它擁有的性質和能力,是全部的對象都有的。自動化
__proto__是什麼意思呢?那是「生」的意思,或者叫作「繼承」。
既然有了一個對象,那麼剩下就好辦了,由於一輩子二,二生三,三生萬物嘛。
可是,造物主很懶,他不想一個一個地親手製造對象。因而他製造了一臺可以造對象的機器:
他給這臺機器起了一個名字:Object。
這臺機器並不能憑空造出對象,它須要一個模板對象,按照這個模板對象來製造對象。很天然的,它把目前僅有的No.1對象做爲模板。圖中的prototype就表明機器的模板對象。
機器又叫作構造函數,爲啥呢?由於它是用來構造對象的嘛。
機器如何啓動呢?答案是經過new命令。你對着機器喊一聲:「new!」,對象就造出來了。
機器的產生,使得生產對象的過程自動化了,解放了造物主的雙手,因而造物主忙別的去了。
若是機器只是按照模板的樣子,機械地複製出如出一轍的對象,那就太笨了。
人類的後代在繼承了父輩的性狀的基礎上,能夠產生父輩沒有的新的性狀。一樣地,機器在製造對象時,除了繼承模板對象的屬性外,還能夠添加新的屬性。
好比說,有一天Object機器製造一個對象,它有一個特殊的屬性,叫作flag,屬性值是10。看起來是這樣的:
寫成代碼就是:
var obj = new Object({ flag: 10 });
一每天過去了,造物主來視察工做,發現Object製造出了不少不少對象。他還注意到:根據「物以類聚」的原則,這些對象能夠分紅不少類。聰明的造物主想,我何很少造幾臺機器,讓每一臺機器負責製造一類對象呢?因而,他造出了幾臺機器並給它們起了名字,分別是:
String:用來製造表示一段文本的對象。
Number:用來製造表示一個數字的對象。
Boolean:用來製造表示是與非的對象。
Array:用來製造有序隊列對象。
Date:用來製造表示一個日期的對象。
Error:用來製造表示一個錯誤的對象。
多臺機器齊開動,各自制造本身負責的那一類對象。轟轟烈烈的造物運動開始了。
造物主又開始思考了,雖然機器是用來製造對象的,可是機器自己實際上也是一種特殊對象啊。如今有了這麼多機器,我得好好總結一下它們的共同特徵,把它們也歸入對象體系。
因而,造物主基於No. 1對象,造出了一個No. 2對象,這個對象用來表示全部機器的共同特質。換句話說,它是全部機器的原型對象。
(注:__proto__寫起來太麻煩了,咱們用[p]來代替。)
固然,同Object同樣,這些機器也須要各自有一個模板對象,即它們的prototype屬性指向的那個對象。顯然,它們的模板對象應該是繼承在No. 1對象的。即
這張圖顯示了JavaScript世界中那些最基本的機器自己的繼承(__proto__)線路以及它們的模板對象的繼承(prototype)線路。只是看起來太複雜了,因此後面咱們再也不把它們的prototype畫出來。
造物主想:這下好了,我造出了Object機器,知足了對象製造的自動化。而後又造出了String、Number等機器,實現了特定類別的對象製造的自動化。可是,彷佛還缺點什麼啊?
對了,還缺乏一臺製造機器的機器啊!很快,萬能的造物主就把它造了出來,並把它命名爲Function。有了Function機器後,就能夠實現自動化地製造機器了。
首先,Function也是一臺機器,因此它的原型對象也是No. 2對象。
其次,Function又是一臺製造機器的機器,因此它的模板對象也是No. 2對象。
因此咱們獲得了Function的一個很是特別的性質:
Function.__proto__ === Function.prototype
哇,太奇妙了!
不要奇怪,這個性質不過是」Function是一臺製造機器的機器「這個事實的必然結果。
因而JavaScript的世界的變成了這個樣子:
從這張圖上,咱們會發現:全部的函數(包括Function)的__proto__都指向No.2 對象,而同時Function.prototype也是No.2 對象。這說明了:
從邏輯上,咱們能夠認爲全部機器(包括Function本身)都是由Function製造出來的。
同時,若是再仔細瞧瞧,你會發現:
Object做爲一個機器能夠看作是有由Function製造出來的,而Function做爲一個對象能夠看作是由Object製造出來的。
這就是JavaScript世界的「雞生蛋,蛋生雞」問題。那麼究竟是誰生了誰呢?Whatever!
根據上文的敘述,機器用來製造某一類對象。正由於如此,機器能夠做爲這類對象的標誌,即面嚮對象語言中類(class)的概念。此時,機器被稱爲構造函數。因此,在ES6引入class關鍵字以前,咱們經常把構造函數叫作類。
然而,除了做爲構造函數來製造對象外,函數一般還用做另外的用途:用來作一件事情。正是有了這個功能,JavaScript的世界才由靜變更,變得生機勃勃。
好比,咱們如今用Function機器製造了鳥類(即用來造鳥的機器):
function Bird(color) { this.color = color; }
而後,對着造鳥機說:「new!」,因而造鳥機發動起來,製造一個紅色的鳥:
var redBird = new Bird('#FF0000');
如今咱們想讓鳥飛起來,因而咱們再用Function機器來製造一臺機器。這臺機器不是用來製造對象的,而是用來作一件事情的,即「讓鳥飛起來」這件事情:
// 這是一臺經過晃動鳥的翅膀,讓鳥飛起來的簡陋的機器。 function makeBirdFly(bird) { shakeBirdWing(bird); }
咱們知道,讓一臺製造對象的機器發動,只須要對它喊「new」便可;那麼怎樣讓一臺作事情的機器發動呢?更簡單,對它咳嗽一聲就好了。
makeBirdFly(redBird);
因而紅鳥飛起來了,世界充滿了生機。
從上面的Bird和makeBirdFly的定義能夠看出:實際上,製造對象的機器和作事情的機器沒什麼明顯區別,它們只是使用方式不一樣。在兩種狀況下,它們分別被叫作構造函數和普通函數。
說明1:function xxx語法能夠當作new Function的等價形式。
說明2:用戶自定義的函數一般既能夠做爲普通函數使用,又能夠做爲構造函數來製造對象。
ES6新增的class語法定義的函數只能做爲構造函數,ES6新增的=>語法定義的箭頭函數只能做爲普通函數。
造物主對目前的世界不太滿意。由於幾乎全部的機器的模板對象都是No. 2,這致使世界看起來有點扁。
因而他又開始研究世界萬物的分類問題。它發現有些對象會動、還會吃東西,因而他把它們叫作動物,用機器Animal來製造它們。他進一步發現,即便都是動物,也仍是能夠進一步分類,好比有些會飛、有些會遊,他分別把它們叫作鳥類、魚類。因而他想,我何不單獨造幾臺機器,專門用來製造某一類動物呢。因而它造出了Bird、Fish等機器。
接下來,在選擇這些機器的模板對象時碰到一個問題:若是還像以前那樣直接複製一個No. 1對象做爲Bird、Fish的模板,那麼結果就是這樣的:
這樣可很差。首先沒體現出鳥類、魚類跟動物的關係,其次它們的模板對象存了重複的東西,這是一種浪費。怎麼辦呢?很簡單,讓Bird和Fish的模板對象繼承自Animal的模板對象就行了。也就是
Bird.prototype.__proto__ === Animal.prototype Fish.prototype.__proto__ === Animal.prototype
因而:
用一樣的方法,造物主造出了一個立體得多的JavaScript世界。
然而還不夠。雖然那些純對象如今充滿了層次感,可是那些機器之間的關係仍是扁平的:
怎麼辦呢?其實用相似的辦法就好了:
爲了作到這點,造物主發明了class關鍵字。
世界如今變得可複雜了,只能畫出一部分: