您好,登錄后才能下訂單哦!
這篇文章主要介紹JavaScript中作用域scope的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
什么是作用域
程序的執(zhí)行,離不開作用域,也必須在作用域中才能將代碼正確的執(zhí)行。
所以作用域到底是什么,通俗的說,可以這樣理解:作用域就是定義變量的位置,是變量和函數(shù)的可訪問范圍,控制著變量和函數(shù)的可見性和生命周期。
而JavaScript中的作用域,在ES6之前和ES6之后,有兩種不同的情況。
ES6之前,JavaScript作用域有兩種:函數(shù)作用域和全局作用域。
ES6之后,JavaScript新增了塊級作用域。
作用域的特性
在JavaScript變量提升的討論中,我們其實是缺少了一個作用域的概念的,變量提升其實也是針對在同一作用域中的代碼來說的。
對編譯器的了解,讓我們明白,對于一段代碼【var a = 10】變量的賦值操作,其實是包含了兩個過程:
1、變量的聲明和隱式賦值(var a = undefined),這個階段在編譯時
2、變量的賦值(a = 10),這個階段在運行時
先看一下如下代碼:
var flag = true; if(flag) { var someStr = 'flag is true'; } function doSomething() { var someStr = 'in doSomething'; var otherStr = 'some other string'; console.log(someStr); console.log(flag); } doSomething(); for(var i = 0; i < 10; i++) { console.log(i); } console.log(i); { var place = 'i do not want to be visited'; }
那么這一些代碼在編譯之后,執(zhí)行之前,根據(jù)變量提升的機制,我們可以知道應該是下面這個樣子:
function doSomething() { // 函數(shù)優(yōu)先提升 // 提升隱式賦值 var someStr = undefined; var otherStr = undefined; someStr = 'in doSomething'; otherStr = 'some other string'; console.log(someStr); console.log(flag); } // 隱式賦值和提升 var flag = undefined; var someStr = undefined; var i = undefined; var place = undefined; flag = true; if(flag) { someStr = 'flag is true'; } for(i = 0; i < 10; i++) { console.log(i); } doSomething(); console.log(i); { place = 'i do not want to be visited'; }
因為變量的提升特性,以及無塊級作用域的概念,所以代碼中在同一個作用域中變量和函數(shù)的定義,在編譯階段都會提升到頂部。
通過上述代碼,我們大體上可以得出作用域的特性:
第一、內部作用域和外部作用域是嵌套關系。外部作用域完全包含內部作用域。
第二、內部作用域可訪問外部作用域的變量,但是外部作用域不能訪問內部作用域的變量,(鏈式繼承,向上部作用域查找)。
第三、變量提升是在同一個作用域內部出現(xiàn)的。
第四、作用域用于編譯器在編譯代碼時候,確定變量和函數(shù)聲明的位置。
塊級作用域
上述代碼,在ES6+的環(huán)境中運行,也是和ES6之前是相同的結果,但是ES6不是引用了塊級作用域嗎,為什么大括號塊內的代碼還是會出現(xiàn)和之前一樣的編譯方式呢?
那么,ES6中的塊級作用域到底是什么?
let & const
利用var定義的變量,具有提升的性質,可能會影響代碼的執(zhí)行結果。
這是var定義變量的缺陷,那么如何規(guī)避這種缺陷呢?在ES6中,設計出來了let和const來重新定變量。
但是,由于JavaScript標準定義的非常早,1995年5月JavaScript方案定義,1996年微軟提供了JavaScript解決方案JScript。而網景公司為了同微軟競爭,神情了JavaScript標準,于是,1997年6月第一個國際標準ECMA-262便頒布了。
C語言標準化的過程卻是將近二十年后才頒布。
所以,我們以后設計的語言既要兼容var也要有自己的塊級作用域,讓var和let以及const在引擎做到兼容。
所以,我們定義塊級作用域的標準,只能從定義變量的方式入手,而不是直接一個{}塊就可以解決。
先讓我們看一下下面代碼:
var name = 'someName'; function doSomething(){ console.log(name); if(true) { var name = 'otherName'; } } doSomething(); 結果:undefined
產生這個結果的原因是我們函數(shù)內部的變量提升,覆蓋了外部作用域的變量,也就是說,其實打印出來的值是doSomething函數(shù)中的變量聲明的值。
但是這樣卻并不符合塊級作用域的預期,如果有許多類似代碼,理解起來也會相當困難。如果將代碼用ES6方式改寫:
let name = 'someName'; function doSomething(){ console.log(name); if(true) { let name = 'otherName'; } } doSomething(); 結果:'someName'
從運行結果看,我們真正的做到了塊級作用域應該有的效果,那么let和const又是如何支持塊作用域的呢?
執(zhí)行上下文
先想想一下JavaScript中的一個作用域兩個執(zhí)行上下文中的編譯過程中的環(huán)境:
變量環(huán)境:編譯階段var聲明存放的位置(一個大對象)。
詞法環(huán)境:我們代碼書寫的位置,也是let和const的初始化位置(代碼按詞法環(huán)境順序執(zhí)行,按照{}劃分的棧結構)。
而在編譯階段,我們將var定義的變量全都在編譯過程在變量環(huán)境初始化為undefined,但是用let和const定義的變量,其實他們并未在變量環(huán)境初始化,而是在詞法環(huán)境初始化,也就是執(zhí)行代碼位置初始化。
詞法環(huán)境的特點:按照{}劃分的一個棧結構。
變量查找方式
JavaScript中變量查找的方式:沿著詞法環(huán)境的棧頂向下查找,找不到的變量去變量環(huán)境中查找,這樣就形成了先查找代碼塊中的變量,再查找提升之后的變量環(huán)境,這樣就形成了塊級作用域的概念。
上面的代碼形成兩種環(huán)境的情況如下:
一、全局環(huán)境的執(zhí)行上下文
變量環(huán)境:函數(shù)聲明function doSomething() { … }
詞法環(huán)境棧:執(zhí)行到let name = ‘someName';讓語句name = ‘someName'入棧。
二、doSomething的執(zhí)行上下文(被全局環(huán)境包裹)
變量環(huán)境:無
詞法環(huán)境棧情況:執(zhí)行到let name = ‘otherName',語句的時候,name = ‘other'才會入棧;
JavaScript代碼執(zhí)行方式
執(zhí)行doSomething的時候,還未執(zhí)行l(wèi)et name = ‘otherName',所以,此時doSomething的詞法環(huán)境中并未有name = ‘otherName',這個時候查找,只能向外部作用域查找(全局作用域)
此時查找到全局作用域name = ‘someName'所以此時就打印了someName
代碼接著執(zhí)行到了if語句內部,才會將name = ‘otherName'入棧,但是此時因為語句已經執(zhí)行完畢,所以也就無關痛癢了。
JavaScript也就通過這種方式,實現(xiàn)了塊級別作用域。
以上是“JavaScript中作用域scope的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業(yè)資訊頻道!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。