Javascript中的作用域和上下文深入理解
概述
Javascript中的作用域和上下文的實(shí)現(xiàn)是Javascript語言獨(dú)有的特性,從某種程度上來說,Javascript語言是十分靈活的。Javascript中的函數(shù)可以采用各種各樣的上下文,作用域也可以被封裝和保存。正是由于這些特性,Javascript中也提供了很多很有用的設(shè)計(jì)模式。然而,作用域和上下文也是Javascript程序員在開發(fā)中經(jīng)常迷惑的地方。
下面會向大家介紹Javascript中作用域和上下文的概念,以及它們的不同。
作用域 VS 上下文
首先要說明的很重要的一點(diǎn)是作用域和上下文并不是同一個(gè)概念,它們指代的并不是同一個(gè)東西。作為一個(gè)前端的菜逼,經(jīng)常會看到一些文章把這兩個(gè)概念弄混,結(jié)果有些東西越看越不明白。這兩個(gè)概念貌似被混淆了很長一段時(shí)間了。因此,查了很多資料,簡單說明下這兩個(gè)概念。:stuck_out_tongue_closed_eyes:
在Javascript中,當(dāng)一個(gè)函數(shù)被調(diào)用時(shí)都會有一個(gè)作用域和上下文和這個(gè)函數(shù)綁定在一起。從根本上來說,作用域是基于函數(shù)的而上下文是基于對象的。換句話說,作用域適用于函數(shù)被調(diào)用時(shí)函數(shù)中變量的訪問權(quán)限。上下文通常是指“this”關(guān)鍵字的值,“this”是擁有當(dāng)前執(zhí)行代碼的對象的引用。
變量作用域
變量可以被定義在局域或全局作用域中,分別稱為局部變量和全局變量。全局變量是指在函數(shù)體外聲明的變量,在程序的任何地方都可以訪問全局變量。局部變量是指在函數(shù)體內(nèi)定義的變量,它僅可以在函數(shù)體內(nèi)或者嵌套的函數(shù)內(nèi)被訪問,并且不能在函數(shù)外部被訪問。
Javascript目前并不支持塊級作用域(在if、switch、for等語句中定義的變量)。這意味著在塊內(nèi)定義的變量,在塊外也可以訪問。但是,在ES6中,我們可以使用“l(fā)et”關(guān)鍵字定義塊級作用域。
關(guān)于作用域的內(nèi)容,大家可以查下別的資料,這部分內(nèi)容相對簡單些。
“this”上下文
上下文(context)通常取決于函數(shù)被調(diào)用的方式。當(dāng)函數(shù)作為對象的方法被調(diào)用時(shí),“this”指代的是調(diào)用該函數(shù)的對象。
var obj={
foo:function (){
console.log(this === obj);
}
};
obj.foo(); //輸出true
同樣,當(dāng)我們使用“new”關(guān)鍵字創(chuàng)建新對象時(shí),this引用的是新創(chuàng)建的對象。
function foo(){
console.log(this);
}
foo(); //輸出window
var obj=new foo(); //輸出 foo {}
有一點(diǎn)需要注意的是,當(dāng)全局作用域中的函數(shù)被被調(diào)用時(shí),this引用的是全局對象,在瀏覽器環(huán)境中指的就是window。但是,如果在嚴(yán)格模式下運(yùn)行代碼時(shí),“this”被設(shè)置為“undefined”
執(zhí)行上下文(Execution Context)
Javascript是單線程的語言,這也就是說Javascript在瀏覽器中運(yùn)行時(shí),一次只能做一件事情,其他的事情將被方法隊(duì)列中,等待被處理。
1.當(dāng)Javascript代碼文件被瀏覽器載入后,默認(rèn)最新進(jìn)入的是一個(gè)全局的執(zhí)行上下文。當(dāng)在全局上下文中調(diào)用一個(gè)函數(shù)時(shí),程序留就進(jìn)入該被調(diào)用函數(shù)內(nèi),此時(shí)Javascript引擎就會為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文,并且將其壓入到執(zhí)行上下文堆棧的頂部。瀏覽器總是執(zhí)行當(dāng)前在堆棧頂部的上下文,一旦執(zhí)行完畢,該上下文就會從堆棧頂部被彈出,然后,進(jìn)入其下的上下文執(zhí)行代碼。這樣,堆棧中的上下文就會被依次執(zhí)行并且彈出堆棧,直到回到全局的上下文。
2.一個(gè)執(zhí)行上下文可以被分為兩個(gè)階段:創(chuàng)建階段和執(zhí)行階段。在創(chuàng)建階段,javascript解釋器首先會創(chuàng)建一個(gè)變量對象(也成為“活動對象”,activation object)。活動對象由變量,函數(shù)聲明和參數(shù)組成。在這個(gè)階段,函數(shù)的作用域鏈被初始化,this引用的對象也被確定。接下來就是執(zhí)行階段,在這個(gè)階段,代碼被解釋并執(zhí)行。
在Javascript代碼中,可以有任意多個(gè)函數(shù)上下文,我們已經(jīng)知道,當(dāng)函數(shù)被調(diào)用時(shí),Javascript解釋器就會創(chuàng)建一個(gè)新的上下文,同時(shí)會創(chuàng)建一個(gè)私有的作用域,函數(shù)內(nèi)部聲明的任何變量都不能在當(dāng)前函數(shù)作用域外部直接訪問。
3.通過上面的解釋,我們對函數(shù)的“執(zhí)行上下文”有了一個(gè)基本的概念,但是這里也是大家最容易迷惑的一個(gè)地方。Javascript中的“執(zhí)行上下文”主要是指作用域,而不是上面第四小節(jié)中指的“this上下文”。類似的容易混淆的概念在Javascript中還有很多,但是我們只要弄清楚了每個(gè)概念所指代的具體對象,就不會再迷惑,因此,這里也希望大家能夠真正的區(qū)分開“執(zhí)行上下文”和“this上下文”。
簡單的一句話概括來說,執(zhí)行上下文是與作用域相關(guān)的概念,雖然這樣說可能不太嚴(yán)謹(jǐn)。
作用域鏈
對每一個(gè)執(zhí)行上下文來說,都有一個(gè)作用域連跟它綁定在一起。作用域鏈包含了執(zhí)行上下文堆棧中的執(zhí)行上下文活動對象(activation object,聽起來有點(diǎn)繞口)。作用域鏈決定了變量的訪問和標(biāo)識符的解析。
代碼示例:
function first(){
second();
function second(){
third();
function third(){
fourth();
function fourth(){
//代碼
}
}
}
}
first();
執(zhí)行上面的代碼,嵌套的函數(shù)都會被執(zhí)行。就上面的代碼來說,也會形成一個(gè)作用域鏈,作用域鏈從頂部到底部的順序?yàn)椋篺ourth, third, second, first, global。函數(shù)fourth可以訪問全局作用域中的變量,并且可以訪問函數(shù)third, second, first中定義的任何變量。
有一點(diǎn)需要注意的是,在函數(shù)體內(nèi),局部變量的優(yōu)先級高于同名的全局變量。如果在函數(shù)內(nèi)聲明的局部變量或函數(shù)參數(shù)中帶有的變量和全局變量重名,那么全局變量就被局部變量所覆蓋。
簡單來說,每次我們嘗試訪問一個(gè)變量時(shí),程序都會在當(dāng)前函數(shù)作用域內(nèi)查找變量,如果找不到就沿著作用域鏈到該函數(shù)的上層去查找,直到找到該變量為止,如果找不到則返回undefined。
總結(jié)
這篇文章介紹了javascript中上下文和作用域的相關(guān)概念,javascript中還有幾個(gè)比較重要的概念,如閉包等,這些在以后自己弄明白了會寫成文章~~
相關(guān)文章
javascript實(shí)現(xiàn)秒表計(jì)時(shí)器的制作方法
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)秒表計(jì)時(shí)器的制作方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02JavaScript trim 去除字符串空格的三種方法(附代碼詳解)
個(gè)人認(rèn)為最好的方法.采用的是正則表達(dá)式,這是最核心的原理.因?yàn)榭崭裼卸喾N形式。2010-05-05獲取當(dāng)前點(diǎn)擊按鈕的id用this.id實(shí)現(xiàn)
這篇文章主要介紹了獲取當(dāng)前點(diǎn)擊按鈕的id的方法,,需要的朋友可以參考下2014-03-03Javascript中的函數(shù)聲明與函數(shù)表達(dá)式(奇技淫巧)
Javascript有很多有趣的用法,在Google Code Search里能找到不少,今天從火丁筆記看到的,非常不錯(cuò),推薦大家看下。2011-03-03迅速了解一下ES10中Object.fromEntries的用法使用
這篇文章主要介紹了迅速了解一下ES10中Object.fromEntries的用法使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03