詳解javascript中的詞法作用域

所謂作用域,就是一個聲明起作用的那一段的程序代碼。而作用域又分為詞法作用域(也叫靜態(tài)作用域)和動態(tài)作用域。靜態(tài)作用域是指在編譯的時候確定的,換句話說就是在聲明時就確定了所綁定的作用域。而動態(tài)作用域是指運(yùn)行時作用域,也就是說根據(jù)程序運(yùn)行時候的信息流來動態(tài)確定的。

詞法作用域(靜態(tài)作用域)

通常,大部分語言都是靜態(tài)作用域,Javascript也不例外,我們來看下面這幾段代碼

var foo='efg';
function a(){
console.log(foo) //會輸出什么呢?
}
function b(){
var foo = 'abc'
a()
}
b();//efg
function a(){
console.log(foo) //會輸出什么呢?
}
function b(){
var foo = 'abc'
a()
}
b();// foo is not defined.

為什么會出現(xiàn)上述的輸出呢,這就是作用域的問題,每個函數(shù)都有自己的執(zhí)行環(huán)境。靜態(tài)作用域代表a()的作用域是在a聲明的時候就確定了自己的作用域,也就是說a的作用域鏈,第一個對象是自己的arguments,第二個對象指向的是全局對象,所以在a()執(zhí)行時查找foo的時候并不會進(jìn)入到b()里面查找foo的值。

動態(tài)作用域

動態(tài)作用域不關(guān)心函數(shù)和作用域在哪聲明和如何聲明,只關(guān)心它們在何處調(diào)用。那么上述的代碼執(zhí)行結(jié)果將會是輸出'abc'。很好理解,在a()執(zhí)行時,需要訪問到foo的時候,是在a的執(zhí)行環(huán)境里面去查找foo,所以在b里面查找了foo的定義和聲明。

javascript中的作用域

javascript簡單明了,只有詞法作用域,但是如eval()、with、this機(jī)制,使用上去很像是動態(tài)作用域,因此使用上要特別注意。

with語句和try-catch語句中的catch塊會加長作用域鏈。在作用域前端添加一個變量對象。

作用域鏈

在B端,全局執(zhí)行環(huán)境被認(rèn)為是window對象,所有的全局變量以及函數(shù)都是作為window的屬性和方法而創(chuàng)建的。當(dāng)代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈。作用域鏈的用途是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。作用域鏈最前端,總是指向當(dāng)前執(zhí)行代碼所在環(huán)境的變量對象。如果這個環(huán)境是一個函數(shù)作用域,則將其活動對象作為變量對象。作用域鏈的下一個變量對象是包含(外部)環(huán)境,直至到全局執(zhí)行環(huán)境,全局執(zhí)行環(huán)境的變量對象始終是作用域鏈中最后一個對象。搜索標(biāo)志符時總是逐級往上,直至找到標(biāo)志符為止。

window
    --foo
    --a()
    --b()
        --foo

Tips:ES6中引入了let方式聲明變量,也就是說引入了塊級作用域。在ES6以前,javascript不存在塊級作用域,所以在例如for if 中聲明的變量會被加入當(dāng)前的執(zhí)行環(huán)境。

Tips:關(guān)于ES6中箭頭函數(shù)(也就是lambda表達(dá)式)中的this其實(shí)是靜態(tài)作用域。

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

友情鏈接更多精彩內(nèi)容