天天看點

JS程式設計建議——57:禁用Function構造函數

建議57:禁用Function構造函數

定義函數的方法包括3種:function語句、Function構造函數和函數直接量。不管用哪種方法定義函數,它們都是Function對象的執行個體,并将繼承Function對象所有預設或自定義的方法和屬性。

// 使用function語句編寫函數

function f(x){

}

// 使用Function()構造函數克隆函數

var f = new Function("x", "return x;");

// 使用函數直接量直接生成函數

var f = function(x){

雖然這些方法定義函數的結構體相同,函數的效果相近,但是也存在很多差異,詳細比較見表3.1。

表3.1 函數定義方法比較

使用function語句 使用Function構造函數 使用函數直接量

相容 完全 JavaScript 1.1及以上 JavaScript 1.2及以上

形式 句子 表達式 表達式

名稱 有名 匿名 匿名

主體 标準文法 字元串 标準文法

性質 靜态 動态 靜态

解析 以指令的形式構造一個函數對象 解析函數體,能夠動态建立一個新的函數對象 以表達式的形式構造一個函數對象

(1)作用域比較

使用Function構造函數建立的函數具有頂級作用域,JavaScript解釋器也總是把它作為頂級函數來編譯,而function語句和函數直接量定義的函數都有自己的作用域(即局部作用域,或稱為函數作用域)。例如:

var n = 1;

function f(){

alert(f()()); //2

在上面示例中,分别在函數體外和函數體内聲明并初始化變量n,然後在函數體内使用function語句定義一個函數e,定義該函數傳回變量n的值。最後在函數體外調用函數的傳回函數。通過結果可以發現,傳回值為局部變量n的值(即為2),也就是說,function語句定義的函數擁有自己的作用域。同理,如果使用函數直接量定義函數e,當調用該傳回函數時,傳回值是2,而不是1,那麼也說明函數直接量定義的函數擁有自己的作用域。

但是,如果使用Function構造函數定義函數e,則調用該傳回函數時,傳回的值是1,而不再是2了,看來Function構造函數定義的函數作用域需要動态确定,而不是在定義函數時确定的,代碼如下:

alert(f()()); //1

(2)解析效率比較

JavaScript解釋器在解析代碼時,并非一行行地分析和執行程式,而是一段段地分析執行。在同一段代碼中,使用function語句和函數直接量定義的函數結構總會被提取出來優先執行。隻有在函數被解析和執行完畢之後,才會按順序執行其他代碼行。但是使用Function構造函數定義的函數并非提前運作,而是在運作時動态地被執行,這也是Function構造函數定義的函數具有頂級作用域的根本原因。

從時間角度審視,function語句和函數直接量定義的函數具有靜态的特性,而Function構造函數定義的函數具有動态的特性。這種解析機制的不同,必然帶來不同的執行效率,這種差異性可以通過将一個循環結構放大來比較得出。

在下面這個示例中,分别把function語句定義的空函數和Function構造函數定義的空函數放在一個循環體内,讓它們空轉十萬次,這樣就會明顯感到使用function語句定義的空函數運作效率更高。

// 測試function語句定義的空函數執行效率

var a = new Date();

var x = a.getTime();

for(var i=0;i<100000;i++){

var b = new Date();

var y = b.getTime();

alert(y-x); //62,不同環境和浏覽器會存在差異

// 測試Function構造函數定義的空函數執行效率

alert(y-x); //2390

JavaScript解釋器首先把function語句定義的函數提取出來進行編譯,這樣每次循環執行該函數時,就不再從頭開始重新編譯該函數對象了,而Function構造函數定義的函數每次循環時都需要動态編譯一次,這樣效率就非常低了。

(3)相容性比較

從相容角度考慮,使用function語句定義函數不用考慮JavaScript版本問題,所有版本都支援這種方法。而Function構造函數隻能在JavaScript 1.1及其以上版本中使用,函數直接量僅在JavaScript 1.2及其以上版本中有效。當然,版本問題現在已經不是問題了。

Function構造函數和函數直接量定義函數方法有點相似,它們都是使用表達式來建立的,而不是通過語句建立的,這樣帶來了很大的靈活性。對于僅使用一次的函數,非常适合使用表達式的方法來建立。

由于Function構造函數和函數直接量定義函數不需要額外的變量,它們直接在表達式中參與運算,是以節省了資源,克服了使用function語句定義函數占用記憶體的弊端,也就是說,這些函數運作完畢即被釋放而不再占用記憶體空間。

對于Function構造函數來說,由于定義函數的主體必須以字元串的形式來表示,使用這種方法定義複雜的函數就顯得有點笨拙,很容易出現文法錯誤。但函數直接量的主體使用标準的JavaScript文法,這樣看來使用函數直接量是一種比較快捷的方法。

通過function語句定義的函數稱為命名式函數、聲明式函數或函數常量,而通過匿名方式定義的函數稱為引用式函數或函數表達式,而把指派給變量的匿名函數稱為函數對象,把該變量稱為函數引用。

繼續閱讀