[JS]《你不知道的Javascript·上》——詞法做用域和閉包

閉包是真的讓人頭暈啊,看了好久仍是以爲很模糊。只能把目前本身的一些理解先寫下來,這其中一定包含着一些錯誤,待往後有更深入的理解時再做更改。閉包

吐槽一下,閉包這個詞的翻譯真是有很大的誤解性啊……函數

要說閉包,要先說下詞法做用域。翻譯

詞法做用域

簡單來講,詞法做用域就是定義在詞法階段的做用域。換句話說,詞法做用域是由你在寫代碼時將變量和塊做用域寫在哪裏來決定的。

不管函數在哪裏被調用,也不管它如何被調用,它的詞法做用域都只由函數被聲明時所處的位置決定。code

閉包

關於閉包

閉包的各個理解:
當函數能夠記住並訪問所在的詞法做用域,即便函數是在當前詞法做用域以外執行,這時就產生了閉包。
做者:Cat Chen
閉包就是由函數創造的一個詞法做用域,裏面建立的變量被引用後,能夠在這個詞法環境以外自由使用。
閉包一般用來建立內部變量,使得這些變量不能被外部隨意修改,同時又能夠經過指定的函數接口來操做。
function foo(){
    var a=2;
    function bar(){
        console.log(a);
    }
    return bar;
}
var baz=foo();
baz();//2——閉包

兩個做用:
(1):經過閉包,在外部環境訪問內部環境的變量。
(2):使得這些變量一直保存在內存中,不會被垃圾回收。接口

上面的代碼例子中,函數在定義時的詞法做用域之外的地方被調用。閉包使得函數能夠繼續訪問定義時的詞法做用域。//?內存

調用的方式不單單隻有以上代碼中的經過不一樣的標識符引用調用其內部的函數,作用域

可是無論經過什麼手段將內部函數傳遞到所在的詞法做用域之外,它都會持有對原始定義做用域的引用,不管在何處執行這個函數都會使用閉包。

循環和閉包

for (var i=1;i<=5;i++){
    setTimeout(function timer(){
        console.log(i);
    },i*1000);
}

正常預想下,上面這段代碼咱們覺得是分別輸出數字1-5,每秒一個。
但實際上,運行時輸出的倒是每秒輸出一個6,一共五次。io

緣由是,延遲函數的回調會在循環結束時才執行。
根據做用域的工做原理,循環中的五個函數是在各個迭代中分別定義的,可是它們都被封閉在一個共享的全局做用域中,實際上只有一個i。console

咱們能夠經過IIFE建立做用域。(IIFE會經過聲明並當即執行一個函數來建立做用域)。function

for (var i=1;i<=5;i++){
    (funtion(){
        setTimeout(function timer(){
            console.log(i);
        },i*1000);
    })();
}

可是這樣建立的做用域裏是空的,須要有本身的變量:

for (var i=1;i<=5;i++){
    (funtion(){
        var j=i;
        setTimeout(function timer(){
            console.log(j);
        },j*1000);
    })();
}

改進獲得:

for (var i=1;i<=5;i++){
    (funtion(j){
        setTimeout(function timer(){
            console.log(j);
        },j*1000);
    })(i);
}

ES6引入的let在循環中不止會被聲明一次,在每次迭代都會聲明:

for (let i=1;i<=5;i++){
    setTimeout(function timer(){
        console.log(i);
    },i*1000);
}

應用場景:模塊

模塊也是利用了閉包的一個強大的代碼模式。

function CoolModule(){
    var something="cool";
    var anothor=[1,2,3];
    
    function doSomething(){
        console.log(something);
    }
    
    function doAnthor(){
        console.log(anothor.join("!"));
    }
    
    return{
        doSomethig:doSomething,
        doAnothor:doAnother
    };
}

var foo=CoolMOdule();
foo.doSomething();//cool
foo.doAnother();//1!2!3
模塊有2個主要特徵:
(1):爲建立內部做用域而調用了一個包裝函數;
(2):包裝函數的返回值必須至少包括一個對內部函數的引用,這樣就會建立涵蓋整個包裝函數內部做用域的閉包。

關於模塊的引入

import能夠將一個模塊中的一個或多個API導入到當前做用域中,並分別綁定在一個變量上; module會將整個模塊的API導入並綁定到一個變量上; export會將當前模塊的一個標識符導出爲公共API。
相關文章
相關標籤/搜索