函數表達式

JavaScript中建立函數主要有兩種方法:函數聲明和函數表達式。這兩種方式都有不一樣的適用場景。這裏主要介紹函數表達式的應用場景。javascript

一.函數遞歸:是在一個函數經過調用名字調用自身的狀況下構成的html

看下面的例子:java

1
2
3
4
5
6
7
function  factorial(num){
     if (num <= 1){
          return  1;
     } else {
          return  num * factorial(num - 1);
     }
}

  這是一個經典的階乘函數,可是這個例子存在的一個問題是函數名稱factorial與函數體緊密耦合在一塊兒,執行下面的語句就會報錯:數組

1
2
3
var  anotherFactorial = factorial;
factorial = null ;
console.log(anotherFactorial(5));   //"Uncaught TypeError: factorial is not a function"

  報錯的緣由在於在函數體內部會調用factorial函數,而變量factorial對函數的引用已經被解除因此報錯。這種狀況的解決方法通常能夠使用arguments.callee來解決,arguments.callee始終指向當前的函數,例如:閉包

1
2
3
4
5
6
7
function  factorial(num){
     if (num <= 1){
          return  1;
     } else {
          return  num * arguments.callee(num - 1);
     }
}

  這樣在此執行anotherFactorial函數就能夠獲得正確結果了。可是在嚴格模式"strict"下,arguments.callee是不能經過腳本訪問的,這是就能夠使用函數表達式來解決這個問題了,例如:函數

 
1
2
3
4
5
6
7
8
var  factorial = ( function  f(num){
     if (num <= 1){
          return  1;
     } else {
          return  num * f(num - 1);
     }
});
console.log(factorial(5));   //"120"

 

二.閉包:是指有權訪問另外一個函數做用域中的變量的函數。建立閉包的常見方式,就是在一個函數內部建立另外一個函數 。this

做用域鏈的這種配置機制引出了一個值得注意的反作用,即閉包只能取得包含函數中任何變量的最後一個值。別忘了閉包所保存的是整個變量對象,而不是某個特殊的變量。下面這個例子能夠清晰地說明這個問題。spa

這個函數會返回一個函數數組。表面上看,彷佛每一個函數都應該返本身的索引值,即位置 0 的函數返回 0,位置 1 的函數返回 1,以此類推。但實際上,每一個函數都返回 10。由於每一個函數的做用域鏈中都 保 存 着 createFunctions() 函 數 的 活 動 對 象 , 所 以 它 們 引 用 的 都 是 同 一 個 變 量 i 。 當createFunctions()函數返回後,變量 i 的值是 10,此時每一個函數都引用着保存變量 i 的同一個變量對象,因此在每一個函數內部 i 的值都是 10。可是,咱們能夠經過建立另外一個匿名函數強制讓閉包的行爲符合預期,以下所示。.net

在重寫了前面的 createFunctions()函數後,每一個函數就會返回各自不一樣的索引值了。在這個版本中,咱們沒有直接把閉包賦值給數組,而是定義了一個匿名函數,並將當即執行該匿名函數的結果賦給數組。這裏的匿名函數有一個參數 num,也就是最終的函數要返回的值。在調用每一個匿名函數時,咱們傳入了變量 i。因爲函數參數是按值傳遞的,因此就會將變量 i 的當前值複製給參數 num。而在這個匿名函數內部,又建立並返回了一個訪問 num 的閉包。這樣一來, result 數組中的每一個函數都有本身num 變量的一個副本,所以就能夠返回各自不一樣的數值了。code

閉包耗費內存的緣由:閉包做用域鏈引用了外部函數的變量對象,閉包沒有執行完畢,做用域鏈引用的外部函數的變量對象都不會被回收,閉包中不須要使用的變量儘早解除引用

閉包能夠用在許多地方。它的最大用處有兩個,一個是能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中。

閉包在訪問外部變量、this、引用HTML元素的時候須要注意:
1.閉包只能取得包含函數中任何變量的最後一個值
2.若是閉包的外部環境是一個函數,那麼閉包中的this是window,若是閉包的外部環境是object,那麼this是這個對象
3.閉包在引用HTML元素時,一旦HTML元素被引用,那麼該元素將沒法被銷燬(內存泄露)

閉包的應用場景:自執行函數、私有屬性和方法以及採用函數引用方式的setTimeout調用

三.模仿塊級做用域:JavaScript中是沒有塊級做用域概念的。也就是說,在塊級語句中定義的變量,其實是在包含函數中(外部函數)而非語句中建立的。
1
2
3
4
5
6
function outputNumber(count){
   for ( var i=0;i<1000;i++){
     alert(i);
   }
   alert(i);  //count
}
 
  

該函數在java、C#等語言中,變量i只會在for循環語句中有定義,循環結束,i也就被銷燬了。但在JavaScript中,變量i是定義在outputNumber()活動對象中的,所以在它定義開始,就能夠在函數內部訪問它。即便從新聲明同一個變量,也不會改變它的值。

匿名函數能夠用來模仿塊級做用域並避免這個問題,用做塊級做用域(也稱私有做用域)的匿名函數的語法以下:

 
1
2
3
( function (){
    //這是塊級做用域
})()

以上代碼定義變調用了一個匿名函數,將函數聲明包含在一個小括號裏面,表示它是個函數表達式。緊跟其後的另外一對小括號會當即調用這個函數。

 

http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

相關文章
相關標籤/搜索