【前端面試系列】JS中的閉包-來龍去脈

引言

經歷過面試的前端人被問得做多的一個話題或許就是js的閉包了吧,就好像高考中總要出些難題來拉開不同人的差距,而閉包就是如此,哪怕平時用的不多,但是因爲其特殊性總被拿來衡量一個前端人的js掌握情況。
以前也陸陸續續看過不少資料,書籍,對其的解釋五碼八門,大差不差,但從來無法回答自己幾個核心問題(例如爲啥叫closure,到底使用場景是什麼),所以一直無法深入去理解,也就更別提靈活的使用了。
最近查閱資料時發現w3school中對其的介紹頗爲深入,翻譯給大家,希望對大家有所啓迪和幫助。

資源地址:https://www.w3schools.com/js/js_function_closures.asp

w3school不清楚原因被牆了,明顯純技術的網站

js中的變量可以是屬於local的或是全局global的。global變量可以利用closure閉包技術變成local本地的。

全局變量

一個函數可以獲取函數內部所有的變量,就像下面這樣(感覺像廢話)

Example
function myFunction() {
    var a = 4;
    return a * a;
}

但函數通過可以獲取外面定義的變量(js特色):

Example
var a = 4;
function myFunction() {
    return a * a;
}

在上一個例子中,a是一個全局的變量。在一個網頁中,全局變量屬於window對象。當然了,全局變量可以在頁面的所有腳本中被使用和修改。在第一個例子中,a是一個局部變量。一個局部變量只能被用在函數定義的地方,對於其他函數和腳本是隔離的。

全局變量和局部變量就算名字取得一樣了,還是不同的變量,改變其中一個,另一個不受影響。

如果變量創建的時候是不帶關鍵字var的,那就都是全局變量,哪怕他們是在函數內部的定義的。

變量的生命週期

全局變量存活的時間就是應用(可以是window或是你的網頁)存在的時間。局部變量生命可就更短了,在幻術被調用的時候創建,然後在函數結束的時候被刪除掉。

A Counter Dilemma(計數器的悖論)

翻譯的不好,其實不是悖論,更確切的說是一種兩難的境地,沒有好的辦法來處理這個計數器,當然後面得通過閉包來實現

假定你想要藉助某個變量來統計某些東西,而且你希望所有的函數都可以使用這個。你當然可以使用一個全局變量和一個函數來實現上面的需求:

Example
// Initiate counter
var counter = 0;

// Function to increment counter
function add() {
    counter += 1;
}

// Call add() 3 times
add();
add();
add();

// The counter should now be 3

上面的方案存在一個問題,頁面上的代碼可以直接修改這個全局變量counter,而且不需要調用add(而我們希望的是counter只作爲add的私有變量來處理)

Example
// Initiate counter
var counter = 0;

// Function to increment counter
function add() {
    var counter; 
    counter += 1;
}

// Call add() 3 times
add();
add();
add();

//The counter should now be 3. But it is 0

完全不起作用,因爲我們顯示的是全局變量的counter而不是局部的counter。我們可以通過讓函數返回它來使刪除全局變量counter而只訪問局部的counter。

Example
// Function to increment counter
function add() {
    var counter; 
    counter += 1;
    return counter;
}

// Call add() 3 times
add();
add();
add();

//The counter should now be 3. But it is 1.

還是不起作用,因爲我們在每次調用函數的時候都重置了局部變量counter。而一個js的內部函數就可以解決這個問題。

JS嵌套函數

所有的函數都可以訪問全局的作用域。事實上,在js中,所有的函數都可以訪問父域的變量。JS支持嵌套函數,嵌套函數就可以訪問父域。在下面的例子中,內部函數plus()可以在父函數中訪問counter變量。

Example
function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();    
    return counter; 
}

這可以解決counter的兩難情況,只要我們可以在外部獲取到plus函數。我們需要讓counter=0只執行一次。因此我們需要closure,閉包。

JS閉包

還記得自調用函數麼,來看看下面的函數:

Example
var add = (function () {
    var counter = 0;
    return function () {counter += 1; return counter}
})();

add();
add();
add();

// the counter is now 3

變量add 設置了自調用函數來返回一個函數。自調用函數正好只執行一次,實現了counter的初始化。這就是一個js的閉包,它能夠讓一個函數有自己私有的變量。counter的作用域被限制在匿名函數中,而且只能通過add函數來改變。所以說閉包就是一個能夠獲取父域的函數,即使父函數已經關了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章