前言
javascript引擎要執行javascript時,會先"預編譯",即對所有宣告的變數及函式進行處理,但要注意的是,在函數中被宣告的local變數只有在函式被呼叫時才會被處理,函數return之後這些變數就會被釋放。函數的定義
函數有三種定義方式:1. 以function宣告(ex. function abc(){})
2. 匿名函數(ex.function(){})
3. Function建構函數(ex. var fn = new Function("return true;"))
前面有提到在預編譯時會先處理被宣告的函數及變數,所以第一種的宣告方式或是將匿名函數指定給一個變數時....
var fn = function(){/*Do something*/};會在預編譯時期先被處理,但在直接執行匿名函數....
(function(){/*Do something*/})();或是用Function建構函數來建立函數時都會在執行到該行程式碼的時候才會被處理。因為這樣的特性使得我們能用Function來比較動態的宣告函數,但因為必須以字串方式傳入函式的敘述內容(函式中要執行的程式碼),所以相對不好維護,而且因為是動態編譯的,效率也會較差,我們可以用一段程式碼來證明:
var startTime = (new Date()).getTime(); for(var i=0; i<100000; i++){ function fn(){} } var total = (new Date()).getTime() - startTime; alert("function*100,000 takes "+total); // 62 secs var startTime = (new Date()).getTime(); for(var i=0; i<100000; i++){ new Function(); } var total = (new Date()).getTime() - startTime; // 18991 secs alert("new Function*100,000 takes "+total);
函數的作用域(scope)
就像上一段所說,因為宣告function會在預編譯時期就被處理,所以函數的作用域是取決於宣告時所在的位置,立即執行函式(IIFE,直接執行匿名函數)也是。但是以Function建構函式所建立的函數作用域是Global。var a = 1; function fn(){ var a=2; var fn1 = function(){alert(a);} var fn2 = new Function("alert(a)"); fn1(); //"2" fn2(); //"1" } fn();
arguments物件
在function中可以使用arguments這個array物件,其中包含了所有被傳入此函數的參數,即使這個函數並不需要任何的輸入,例:function fn(){ for(var i in arguments){alert(arguments[i]);} } fn(1,2,3,4,5,6,7,8,9);順帶一提,要如何取得一個函數預定接收幾個參數呢?可以使用function.length
.call() & .apply()
之前剛好寫了一篇關於這兩個方法的文章。這兩個方法能讓function很靈活的視作不同object的屬性來使用,切換不同的scope,用完之後馬上消失,不會真的成為該object的屬性。
別忘了function物件也有屬性
function也是個物件,當然也有自己的屬性。下面的這段程式碼想用迴圈來依次輸出1~10,但這樣的寫法用到了全域變數,缺乏封閉性。function fn(){ return x++; } var x = 1; for(var i=1; i<=10; i++){ alert(fn()); }所以我們可以利用函數自己的屬性來暫存這個值
function fn(){ return fn.x++; } fn.x = 1; for(var i=1; i<=10; i++){ alert(fn()); }
閉包(Closure)
在C、JAVA等語言中,當呼叫函數並傳回後,所有的區域變數就會被釋放,即刪除他們所在的堆疊。但在Javascript裡,如果宣告了一個內嵌函數,區域變數將在函數傳回後仍能被存取。我們可以利用這種特性來設計具有封閉及包裹性的閉包架構:var mod = (function(){ var d=0; var foo=function(){return d;}; return foo; })(); alert(mod()); // "0",還是存取得到閉包中的變數關於Javascript的模組化可看一下之前的這篇文章。