關於閉包和做用域的問題

首先先引用《JavaScript權威指南》裏面的一句話來開始個人博客:函數的執行依賴於變量做用域,這個做用域是在函數定義時決定的,而不是函數調用時決定的。閉包

所以,就出現了以下的幾串代碼:ide

var a="outside";
function area(){
    var a="inside";
    function b(){
        return a;
    }
    return b();
}
area();

結果爲:inside函數

這樣的結果並不太出乎意料,由於在執行area()的時候,返回值就已是b()了,也就是函數b已是調用以後才被返回的,固然返回的是局部變量a。spa

那若是我返回的值不是執行函數b後的結果,而是函數b呢?code

var a="outside";
function area(){
    var a="inside";
    function b(){
        return a;
    }
    return b;
}
area()();

結果仍是inside對象

js函數的執行用到了做用域鏈,而做用域鏈是在函數定義的時候建立的,在上面兩個例子中,函數b()是定義在局部做用域裏面的,也就是說,它的返回值a早就註定是局部變量的a了,不管外面的area()函數的返回值是否爲執行過的函數b()的結果。其實這個就是閉包,爲何閉包可以讓局部變量的值始終保持在內存中?《JavaScript權威指南》裏面有這樣一段話:每次調用函數,都會爲之建立一個新的對象來保存局部變量,而後把該對象添加至做用域鏈中(每次調用就建立一個新的,調用多少次,建立多少個,執行結果互不影響)。當函數返回時,原本應該是直接從做用域鏈中將這個對象刪除,可是閉包的出現讓這一切變得不簡單。當返回的是一個嵌套函數的時候,就會有一個外部的引用指向這個嵌套的函數,能夠理解爲外部對它進行調用,或者賦值給某個變量,在js垃圾回收機制中,一旦某個變量再也不被引用,那麼這個變量將會被回收。因而可知以前綁定在做用域鏈上的對象因爲閉包的關係不會被當作垃圾回收,這也就是閉包可以讓局部變量的值始終保持在內存中的緣由。blog

接下來咱們來看一下幾段有關於閉包和做用域的代碼,這幾段代碼都採用自權威指南。ip

function add(){
    var num=0;
    return {
        count:function(){return num++;},
        reset:function(){num=0;}
    };
}
var a=add(),b=add(); //建立兩個計時器
a.count(); //0       第一行
b.count(); //0       第二行
a.reset(); //重置    第三行
a.count(); //0       第四行
b.count(); //1       第五行

這段代碼的結果正好印證了每次調用就會建立不一樣的對象,而後保存在做用域鏈上。第一行和第二行是兩個計時器對計時函數的調用,很明顯他們互不影響,第三行a計時器進行重置,固然對b計時器無效了,互不影響的嘛!內存

下面這兩段代碼是咱們常常碰到的筆試題作用域

function a(v){
    return function(){return v;};
}
var funs=[];
for(var i=0;i<10;i++){
    funs[i]=a(i);
}
console.log(funs[5]()); //5
function a(){
    var funs=[];
    for(var i=0;i<10;i++){
        funs[i]=function(){
            return i;
        }
    }
    return funs;
}
var funs=a();
console.log(funs[5]()); //10 

第一段代碼執行結果爲5,第二段爲10。緣由能夠根據前面的篇幅來解釋。

第一段代碼只有一個閉包,可是有10個外部調用函數,也就是10個對象保存在做用域鏈上,執行結果互不干擾,因此當調用funs[5]() 的時候,結果確定也是5。

第二段代碼有十個閉包,共享同一個外部函數的局部變量,外部調用函數只有一個,當9次循環執行完以後,i還被++了,因此結果是10,因此外部函數調用到內嵌函數的時候,結果爲10。

相關文章
相關標籤/搜索