構造器函數(Constructor functions)的定義和任何其它函數同樣,咱們可使用函數聲明、函數表達式或者函數構造器(見之前的隨筆)等方式來構造函數對象。函數構造器和其它函數的區別在與它們的調用方式不一樣。javascript
要以構造函數的方式調用函數,只須要在調用時在函數名稱前加new 關鍵字,好比:function whatsMyContext(){ return this; }; 調用:new whatsMyContext();java
以構造函數的方式調用函數是JavaScript中很是強大的特性,下面來探究它的神祕之處:函數
function Ninja() { this.skulk = function() { return this; }; } var ninja1 = new Ninja(); var ninja2 = new Ninja(); assert(ninja1.skulk() === ninja1, "The 1st ninja is skulking"); assert(ninja2.skulk() === ninja2, "The 2nd ninja is skulking");
上面的demo中,首先定義了一個構造器函數,這個構造器函數會在函數的上下文對象上建立一個名爲shulk的函數屬性。接着經過以構造函數的方式調用函數Ninja來建立兩個Ninja(類型)對象,而且分別被ninja1和ninja2引用,最後經過自定義函數assert來驗證每一個對象的skulk方法,每一個shulk方法應該返回每次以new的方式調用Ninja時構造出來的函數對象。測試
下面來看一下這個過程當中發生了什麼:this
1.一個全新的空的對象被建立;spa
2.這個空的對象被傳遞到構造器函數而且做爲它的this參數,所以成爲了這個構造器函數的上下文對象;code
3.在這個新的空的對象上添加屬性,shulk對象
4.這個被構造出來的對象被做爲函數的返回值返回。blog
使用構造器函數的目的是爲了建立新的對象,對這個對象進行操做而且這個對象做爲構造器函數的返回值返回。任何和這個目的無關的函數都不適合做爲構造器函數。在函數沒有返回值的時候以構造函數的方式調用這個函數會返回新建立的上下文對象,那麼在函數返回一個值(基本類型)的時候會有什麼變化嗎?看下面的例子:ip
function Ninja() { this.skulk = function () { return true; }; return 1 } assert(Ninja() === 1,"Return value honored when not called as a constructor"); var ninja = new Ninja(); assert(typeof ninja === "object","Object returned when called as a constructor"); assert(typeof ninja.skulk === "function","ninja object has a skulk method");
運行上面的例子,全部的斷言均可經過,事實上Ninja函數返回一個簡單類型的值1對代碼的執行沒有產生任何影響:若是以函數的方式調用Ninja它將返回1,而以構造函數的方式調用它,會返回new產生的函數上下文對象。 若是函數返回另外一個對象會發生什麼?
var puppet = { rules: false }; function Emperor() { this.rules = true; return puppet; } var emperor = new Emperor(); assert(emperor === puppet, "The emperor is merely a puppet!"); assert(emperor.rules === false, "The puppet does not know how to rule!");
此次和上面的例子不一樣,首先建立一個puppet對象並把它的rules屬性設置爲false;接着定義一個名爲Emperor的函數,而且把它的上下文變量的rules屬性設置爲true,可是函數最後卻把全局的puppet對象做爲返回值返回。再接着以構造函數的方式調用Emperor函數;這樣產生一個有歧義的狀況,咱們得到了一個做爲函數上下文的對象(this),可是卻返回了一個徹底不一樣的對象,這種狀況下哪個對象會被返回呢?
根據後面assert函數的斷言測試能夠看到,被返回的是puppet函數。下面來總結一下:
1.若是構造器函數返回一個對象,這個對象會做爲new表達式的返回值返回,在以new方式調用函數時產生的做爲函數上下文(this)的對象會被丟棄;
2.若是構造器函數沒有返回對象(返回值爲值類型),這個返回的值會被丟棄,以new方式調用函數時產生的做爲函數上下文(this)的對象會被返回。
構造器函數的代碼習慣:
構造器函數的目的是爲函數調用時產生的上下文對象進行初始化並返回這個對象,雖然這些構造器函數能夠以函數或者方法的形式調用,一般狀況下那麼作並無什麼意義,好比:
function Ninja() { this.skulk = function() { return this; }; } var whatever = Ninja();
簡單的以函數方式調用Ninja,skulk會被綁定到全局變量window上(非嚴格模式下),這不是特別有用的操做,在嚴格模式下更沒有意義,由於上下文對象this爲undefined,所以會拋出不能爲undefined設置屬性的異常。在非嚴格模式下,這樣作很容易出現莫名其妙的情況,這很難被察覺到由於它沒有拋出任何異常。
由於構造器函數也是普通的函數,只是使用不一樣的方式調用而已,一般狀況下,對於構造器函數的定義有不一樣的命名習慣:
以動詞開頭用以描述它是來作什麼的而且首字母小寫的函數名適合普通的函數或者對象的方法;構造器函數的命名一般是一個名詞,用來描述要建立的對象,首字母一般大寫。