Javascript閉包(1)

變量做用域

簡而言之,JS變量存在於兩個做用域裏,即全局做用域和函數內的局部做用域。全局做用域的變量是經過在容器(如瀏覽器的window)裏的定義的,局部做用域是定義在函數內部。瀏覽器

做用域鏈

首先,函數可使用全局做用域的變量。其次,函數內部能夠定義嵌套的子函數,而嵌套的函數能夠遞歸般的使用上一級函數做用域內的變量直至全局變量,這就構成了一個做用域鏈。閉包

做用域鏈的打破

每一個函數的綁定都生成並維持了一個閉包,一般狀況下,一個函數被調用以後,這個閉包環境就會被回收。可是若是函數在調用的時候,返回了一些內部的函數,那麼等因而創建了一個通道,將內部做用域暴露給外部,從而打破了做用域鏈。造成了一條區別於自內而外到做用域鏈的新鏈接。函數

var F = function(){
        var b = 2;
        return function(){
            b += 1;
            return b;
        };
    }
    var inner = F();

    window.console.log(inner());//3
    window.console.log(inner());//4
    
    var another = F();
    window.console.log(another()); //3
    window.console.log(inner());//5

上述代碼,能夠看出一下兩點:code

  1. F()返回的是一個函數,這個函數調用的結果是返回b的值。這個做用域鏈inner->function->b,所以inner=F()後,就產生了一個新的做用域,維持着b這個局部變量不被回收。兩次調用,不一樣的返回值,說明b還在做用域內,沒有被回收。
  2. 做用域鏈是相互獨立的,inner和another維護着兩個獨立的變量做用域鏈。

閉包的本質

來看一個經典的例子:遞歸

function F(){
    var arr = [],i;
    for(i = 0; i < 3; i++){
            arr[i] = function(){
                    return i;
           }
        }
    return arr;
}
window.console.log(arr[0]());    //3
window.console.log(arr[1]());    //3
window.console.log(arr[2]());    //3

上述代碼,arr裏包含了三個函數,都是 return i。 可是這個i的值,爲何是3,而不是0,1,2呢?這進一步說明,閉包維護的是變量之間的引用關係,而不是這些變量在定義的時候的值。那麼如何使arr如預期那樣工做呢?其實目標就是讓arr存的三個函數有獨立的做用域鏈。如上一節分析的那樣,每次函數的調用,會創建一個做用域鏈,所以只要使arr裏的三個變量是三次函數調用,就能夠創建三個獨立的做用域鏈。那麼如何使arr的元素從函數變成函數調用呢?嘗試如下兩個方法: 1.當即調用函數作用域

function FF(){
        var arr = [];
        for(var i = 0; i < 3; i++){
            arr[i] = (function(x){
                return function(){
                    return x;
                }
            })(i);
        }
        return arr;
    }
    var arr = FF();

    window.console.log(arr[0]());//0
    window.console.log(arr[1]());//1
    window.console.log(arr[2]());//2
  1. 內部函數
function FF(){
        var arr = [];
        function proxy(x){
            return function(){
                return x;
            }
        }
        for(var i = 0; i < 3; i++){
            arr[i] = proxy(i);
        }
        return arr;
    }
    var arr = FF();

    window.console.log(arr[0]());
    window.console.log(arr[1]());
    window.console.log(arr[2]());

其實兩個方法本質上是一個方法。io

相關文章
相關標籤/搜索