作用域和閉包

作用域鏈

(據(jù)我所知)所有的編程語言都存在作用域鏈。整個代碼存在全局作用域、函數(shù)作用以及塊級作用域。

var a = 1
function foo(){
    var b = 2
    function bar(){
        console.log(a,b)
    }
    bar()
}
foo()

上述代碼將會打印1和2。
可以看到上面的代碼有三個作用域。全局作用域,foo函數(shù)內部的作用域,bar函數(shù)內部的作用域,它們的關系如下圖。


在函數(shù)bar中,可以訪問foo作用域和全局作用域的變量,這種作用域的包含關系就被稱為作用域鏈。

詞法作用域

一個函數(shù)的作用域有兩種設計方式

  • 詞法作用域:根據(jù)函數(shù)被聲明的位置來決定它的作用域
  • 動態(tài)作用域:根據(jù)函數(shù)被執(zhí)行的位置來決定它的作用域

參考下面的代碼:


function foo(){
    var a = 3
    function bar(){
        console.log(a)
    }
    return bar
}

function foo1(){
    var a = 4
    var bar1 = foo()
    bar1()
}
foo1()

其實我們可以明顯的看到,上述代碼實際執(zhí)行中就是bar函數(shù)被執(zhí)行了。我們也可以看到,bar被定義的時候是在foo函數(shù)內部,但是它被執(zhí)行的時候卻是在foo1內部。那么bar中的a到底是3還是4呢?

其實,JS和大多數(shù)編程語言一樣,也遵守詞法作用域的規(guī)則。所以上述代碼的結果為3。

作用域鏈和函數(shù)調用棧

很多人會把作用域鏈和函數(shù)調用棧弄混淆。
上述代碼函數(shù)調用棧的變化:


可以看到,在bar函數(shù)執(zhí)行的時候,foo函數(shù)已經出棧了。所以有的同學認為foo函數(shù)中的局部變量a也就跟著出棧、銷毀了。
其實作用鏈和函數(shù)調用棧沒有任何聯(lián)系,這兩個概念是獨立的系統(tǒng)。JS查找變量的值時候并不是按照函數(shù)調用棧來進行查找的,而是通過作用域鏈,而且查找時符合詞法作用域規(guī)則。

不同的引擎有不同的方式來實現(xiàn)這一點。比如在V8中,。。。。。

作用域示意圖

閉包

上述的現(xiàn)象也就是人們經常說的閉包:根據(jù)函數(shù)作用域鏈而不是函數(shù)調用棧來查找變量。只要是采取詞法作用域規(guī)則的函數(shù)式編程語言,都必定支持閉包。
這是JS


function foo(){
    var a = 3
    function bar(){
        console.log(a)
    }
    return bar
}

var biu = foo()
biu() // 3

這是python

def foo():
    a = 3
    def bar():
        print(a)
    return bar

biu = foo()
biu() // 3

go

package main

import "fmt"

func foo() func(){
    var a = 3
    return func(){
        fmt.Println(a)
    }
};

func main(){
    var biu = foo()
    biu() // 3
}

可以看到,這些語言都支持閉包。當然,向C++,Java這種不允許將函數(shù)作為變量的語言,也就不再閉包的概念了。

閉包的應用

閉包在for -var中的應用

考慮這樣的代碼

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

有的人希望隔10毫秒打印出0、1;但其實上述代碼會打印出2、2
產生這種現(xiàn)象的原因是var聲明的變量沒有塊級作用域,回調函數(shù)被執(zhí)行的時候i的值已經是2了。
要解決上面的問題,可以將var修改為let,或者使用閉包。

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

閉包在前端模塊化的作用

nodejs 模塊化的原理

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 之所以寫這篇文章,是跟我經歷有關,前兩天面試碰到一個很無理的面試官,年紀不大,電話面試,說話傲氣,自稱是寫reac...
    前端小學生_f675閱讀 794評論 0 1
  • 變量 變量分為全局變量和局部變量,全局變量就是指該變量的作用域為當前文檔,也就是說全局變量在當前文檔的所有Java...
    jrg陳咪咪sunny閱讀 442評論 0 1
  • JavaScript作用域鏈 作用域 作用域就是變量和函數(shù)的可訪問范圍, 控制著變量和函數(shù)的可見性與生命周期, 在...
    HelloJames閱讀 392評論 0 1
  • 1、先理解一下作用域 如果我們初始化一個變量,比如:var a = 1;參與這段代碼執(zhí)行的幾個角色包括: 引擎:從...
    snow_in閱讀 237評論 0 0
  • 文/凡塵追夢 持續(xù)分享第13天 2017.1028 晴 《一根無形的指揮棒》 昨天實戰(zhàn)課王老師給我們做了一個...
    壹凡心理閱讀 215評論 0 0

友情鏈接更多精彩內容