淺談JS閉包

語言: CN / TW / HK

時隔一年重新回過頭來看JavaScript中的相關核心概念,閉包始終是一個不管是面試還是技術交流的時候都無法避免的一個老生常談的話題。我記得差不多4年前開始接觸前端的時候,總會被閉包這個概念給搞迷糊,每次閱讀閉包相關的文章的時候,看完後覺得自己懂了,後來每當被別人問起“什麼是閉包“的時候,給出的解釋連自己都不能夠被說服。 似懂非懂。相信有一些同學和我有同樣的感受,這邊文章裡面咱們就簡單的交流一下什麼是閉包。 

WHAT 是閉包

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

簡單來說,閉包是由函式以及宣告該函式的詞法環境組合而成的。 該環境包含了這個閉包建立時作用域的任何區域性變數。閉包是一個組合,它包含了函式和圍繞該函式的詞法環境

舉個例子,下面的例子中最終的輸出結果是什麼呢? 

for( var i = 0; i < 3; i++ ) {
    setTimeout(() => {
        console.log( i );
    }, 1000)
}

這個問題相信很多人都知道答案,結果是並不是 0,1,2 而是 2,2,2

上面的程式碼如何調整才能達到我們期望的輸出呢,答案是建立相互獨立的閉包,換句話就是更改並重新構建一個圍繞setTimeout 函式的作用域/詞法環境。 下面我簡單介紹兩種方式來達到上面的目的。 

//方法一 
for(var i = 0; i < 3; i ++){
    (function(j){
        setTimeout(()=>{
            console.log(j)
        },1000)
    })(i)
}

//方法二 es6
for(let i = 0; i < 3; i ++){
    setTimeout(()=>{
        console.log(i)
    },1000)
}

經典測試題 

function fun(n, o) {
    console.log(o);
    return {
        fun: function(m) {
            return fun(m, n);
        }
    };
}

// var a = fun(0); a.fun(1);  a.fun(2); a.fun(3);  // 輸出 ???  
// var b = fun(0).fun(1).fun(2).fun(3)  // 輸出 ??? 
// var c = fun(0).fun(1); c.fun(2); c.fun(3); // 輸出 ???

正確答案

// 第一行  undefined 0 0 0 
// 第二行  undefined 0 1 2
// 第三行  undefined 0 1 1

總結

閉包無處不在,javascript 是function-first的面嚮物件語言,如何正確的理解並分析當前函式的詞法環境 or 作用域Scope是理解閉包的關鍵。

希望各位童鞋,可以多給一些反饋。如果覺得這篇文章還不錯的話,麻煩給一個贊謝謝😄 happy coding.

「其他文章」