上一篇文章中 介紹了function在javascirpt作為一等公民所擔任的重要責任,在不同 的上下文中它擔任着不同的角色,
在對象內部它可以是函數,同時又能充當名字空間,不僅如此所有的function都是閉包。看起來它的確是了不得,
不過除 此之外,function還能擔當構造函數,亦或者說它同時還是一個類的聲明。
這篇文章的目的向大家詳細介紹function是如何作為構造函數。
如何定義一個函數
- 聲明式
函數定義最常用的方式之一。
1 //聲明函數 2 function add(x, y) { 3 return x + y; 4 } 5 6 //聲明構造函數 7 function Animal(name, age) { 8 this.name = name; 9 this.age = age; 10 } 11 12 Animal.prototype.bark = function() {return 'bark'};
其實這兩種發式完全一樣,沒有任何區別,你同樣可以為add的prototype屬性增加新的函數或者屬性。
不過兩種方式是基於不同的用途產生的,我們之所以稱后者為構造函數,僅僅因為它的命名和函數實現。
命名: 對於構造函數我們遵循首字母大學的規則以區別普通函數和方法,這和java中對於類的定義是非常相似的。
實現: 構造函數是我們用來生產實例對象的工具,它通長與new關鍵字連用,如
1 var dog = new Animal('pipi', 3); 2 dog; // Animal {name: "pipi", age: 3}
同時你可能也注意到了Animal中使用到了this關鍵字,通長情況下this會指向函數的調用對象,
也就是說如果你在瀏覽器中運行一下代碼,你應該得到window對象:
1 function getThis() {return this}; 2 getThis(); //window
試想一下,如果你這樣使用構造函數:
1 var dog = Animal('pipi', 3);
這段代碼會產生什么有趣的結果呢?
恭喜你!你中招了!
你會發現,你的window對象新增加了兩個屬性, 同時沒有任何對象產生:
1 window.name === 'pipi'; //true 2 window.age === 3 //true 3 dog === undefined; //true
對比使用構造函數的兩種方式,其實這都是new在作怪,當我們在像使用java類那樣對構造函數new出javascript對象時,
它幫我們完成了一些magic(這也是javascript new關鍵字被人詬病的地方),我們不妨來一次魔術揭秘:
//魔術 var dog = new Animal('pipi', 3); //揭秘 function newAlternative(constructor, name, age) { var obj = {}; //修改this,指向新對象obj constructor.call(obj, name, age); //使實例對象繼承構造函數 obj.__proto__ = constructor.prototype; return obj; } var dog = newAlternative(Animal, 'pipi', 3); dog; //Animal {name: "pipi", age: 3, bark: function}
因此,我們需要非常細心的處理函數,如果它就是‘類’那么請根據潛規則,首字母大寫它,並且在使用的時候一定要與new連用。
- 匿名
匿名函數廣泛在javascript代碼中使用,先看看這段代碼:
1 //定義處理元素的匿名函數 2 [1,2].map(function(e) {return e + 2}); 3 4 //定義匿名構造函數 5 var Animal = function(name, age) { 6 this.name = name; 7 this.age = age; 8 } 9 Animal.prototype.bark = function() {return 'bark'}; 10 11 Animal.name === ''; //true
- Function
相對與前兩種方式,使用Function來定義一個新的function相對少見。如果說構造函數是為生成實例對象而生,
那么Function就是為生成函數而生,它是生成函數的模板,構造函數,‘類’.
1 var Animal = 2 new Function('name', 'age', 'this.name = name; this.age = age;'); 3 Animal.prototype.bark = function() {return 'bark'};
雖然這種方式在實際開發中很少被用到,但是它對我們理解函數的生成過程有幫助作用,對比一下兩組代碼,
我們不難發現Animal其實就是Function的實例對象,就如同dog是Animal的實例對象一樣。
1 Animal.__proto__ === Function.prototype //true 2 3 var dog = new Animal('pipi', 3); 4 dog.__proto__ === Animal.prototype; //true
總結
函數在javascirpt中起着至關重要的作用,構造函數是其中一種特殊的函數,他通過和new關鍵字搭配使用,
幫助我們完成看起來與普通面向對 象相似的對象構造過程,不過這僅僅是想像而已,我們必須理解其中的奧秘才能是我們不會在編碼過程中犯錯。
有了對函數的理解,接下來我們變可以將解javascript中的另外一個非常關鍵的語言特性,繼承機制–原型鏈,敬請期待。