
如何快速入門VUE3.0:進入學習
相關推薦:javascript教學
我們大概常常能聽到「執行環境」、「作用域」、「原型(鏈)」、「執行上下文」等內容,它們都在描述什麼?
我們知道了js是弱型別語言,在運行時才確定變數類型。 js引擎在執行js程式碼時,也會從上到下進行詞法分析、語法分析、語意分析等處理,並在程式碼解析完成後產生AST(抽象語法樹),最後根據AST產生CPU可以執行的機器碼並執行。
除此之外,JS引擎在執行程式碼時還會進行其它處理,如V8 中還有兩個階段:
這就引出了兩個概念:「執行上下文」 和「作用域鏈」。
由上面我們可以知道:當js程式碼執行一段可執行程式碼時,就會建立對應的執行上下文。
首先,js中可執行程式碼對應著有一個概念:「執行環境」 —— 全域環境、函數環境和eval 。
其次,對於每個執行上下文,都有三個重要屬性:
我們來看兩段程式碼:
var scope="global scope";function checkscope(){
var scope="local scope";
function f(){
return scope;
}
return f();}checkscope(); var scope="global scope";function checkscope(){
var scope="local scope";
function f(){
return scope;
}
return f;}checkscope()();它們會印什麼?
為什麼?答案是它們的執行上下文堆疊不一樣!
什麼是「執行上下文棧」?
當執行一個可執行程式碼時,就會提前做準備工作,這裡的“準備工作”,專業的說法就是“執行上下文”。但隨著可執行程式碼如函數的增多,如何管理那麼多的執行上下文?所以JS引擎創建了執行上下文堆疊的概念。
我們完全可以用陣列去模擬其行為(棧底永遠有一個全域執行上下文globalContext)
我們定義一個EStack,首先
EStack=[globalContext];
然後來模擬第一段程式碼:
EStack.push(<checkscope> functionContext); EStack.push(<f> functionContext);EStack.pop();EStack.pop();
而第二段程式碼是這樣的:
EStack.push(<checkscope> functionContext);EStack.pop();EStack.push (<f> functionContext);EStack.pop();
究其原因,你可能需要先研究一下「閉包」的概念了!
這裡順便說下「在前端模組化」怎麼實現「長時間保存資料」?
快取?不。閉包!
首先,作用域是指程式中定義變數的區域。作用域規定如何找出變量,也就是確定了目前執行程式碼對變數的存取權限。
作用域有兩種:靜態作用域和動態作用域。
JS採用的靜態作用域,也叫「詞法作用域」。函數的作用域在函數定義的時候就確定了。
由上,詞法作用域中的變量,在編譯過程中會產生一個確定的作用範圍。這個作用範圍即「目前的執行上下文」。在ES5後我們用「詞法環境」取代作用域來描述該執行上下文。詞法環境由兩個成員組成:
我們仍然來看一個例子:
var value=1 ;function foo(){
console.log(value);}function bar(){
var value=2;
foo();}bar();回看上面的定義,該印什麼?

讓我們分析下執行流程:
執行foo()函數,先從foo函數內部找出是否有局部變數value。如果沒有,就根據定義時的位置,找出上面一層的程式碼,也就是value=1.所以結果會列印1。
這裡面當然不是如此簡單能概括的,你可以從執行上下文的角度來分析。
上面我們說了詞法環境(作用域)的兩個組成。再結合執行上下文,我們不難發現:透過外部詞法環境的引用,作用域可以順著堆疊層層拓展,建立起從當前環境向外延伸的一條鍊式結構。
再來看一個例子:
function foo(){
console.dir(bar);
var a=1;
function bar(){
a=2;
}}console.dir(foo);foo();由靜態作用域,全域函數foo創建了一個自身物件的[[scope]]屬性
foo[[scope]]=[globalContext];
而當我們執行foo( )時,也會先後進入foo函數的定義期和執行期。在foo函數的定義期時,函數bar的[[scope]]將會包含全域內建scope和foo的內建scope
bar[[scope]]=[fooContext,globalContext];
這證明了這一點:「JS會通過外部詞法環境引用來創建變數物件的一個作用域鏈,從而保證對執行環境有權存取的變數和函數的有序存取
。了它們有什麼不同,這裡說下為什麼它們相同地打印了“local scope”:還是那句話“JS採用的是詞法作用域,函數的作用域取決於函數創建的位置” —— JS函數的執行用到了作用域鏈,這個作用域鍊是在函數定義的時候建立的。巢狀的函數f() 定義在這個作用域鏈裡,其中的變數scope一定是指局部變量,不管何時何地執行f() ,這種綁定在執行f() 時依然有效。
當某個變數無法在自身詞法環境記錄中找到時,可以根據外部詞法環境引用向外層進行尋找,直到最外層的詞法環境中外部詞法環境引用為null 。
與此相似的是「物件中基於原型鏈的查找」:
__proto__為null)為止它們的差異也顯而易見:原型鍊是透過prototype 屬性建立物件繼承的連結;而作用域鍊是指內部函數能存取到外部函數的閉包。不管直接或間接,所有函數的作用域鏈最終都會連結到全域上下文。
相關推薦:javascript學習教學
以上就是深入了解JavaScript引擎如何執行JS程式碼的詳細內容,而更多請關注php中文網其它相關文章!
