摘要:瀏覽器總是運行位于作用域鏈頂部的當前執行上下文。不同執行上下文之間的變量命名沖突通過攀爬作用域鏈解決,從局部直到全局。它將攀爬作用域鏈檢查每一個執行上下文的變量對象,尋找和變量名稱匹配的值。
1>什么是執行上下文
Javascript中代碼的運行環境分為以下三種:
全局級別的代碼 - 這個是默認的代碼運行環境,一旦代碼被載入,引擎最先進入的就是這個環境。
函數級別的代碼 - 當執行一個函數時,運行函數體中的代碼。
Eval的代碼 - 在Eval函數內運行的代碼。
javascript是一個單線程語言,這意味著在瀏覽器中同時只能做一件事情。當javascript解釋器初始執行代碼,它首先默認進入全局上下文。每次調用一個函數將會創建一個新的執行上下文。
每次新創建的一個執行上下文會被添加到作用域鏈的頂部,有時也稱為執行或調用棧。瀏覽器總是運行位于作用域鏈頂部的當前執行上下文。一旦完成,當前執行上下文將從棧頂被移除并且將控制權歸還給之前的執行上下文。
不同執行上下文之間的變量命名沖突通過攀爬作用域鏈解決,從局部直到全局。這意味著具有相同名稱的局部變量在作用域鏈中有更高的優先級。
簡單的說,每次你試圖訪問函數執行上下文中的變量時,查找進程總是從自己的變量對象開始。如果在自己的變量對象中沒發現要查找的變量,繼續搜索作用域鏈。它將攀爬作用域鏈檢查每一個執行上下文的變量對象,尋找和變量名稱匹配的值。
2>執行上下文的建立過程
我們現在已經知道,每當調用一個函數時,一個新的執行上下文就會被創建出來。然而,在javascript引擎內部,這個上下文的創建過程具體分為兩個階段:
建立階段(發生在當調用一個函數時,但是在執行函數體內的具體代碼以前)
建立變量,函數,arguments對象,參數
建立作用域鏈
確定this的值
代碼執行階段:
變量賦值,函數引用,執行其它代碼
實際上,可以把執行上下文看做一個對象,其下包含了以上3個屬性:
executionContextObj = { variableObject: { /* 函數中的arguments對象, 參數,
內部的變量以及函數聲明 / }, scopeChain: { / variableObject
以及所有父執行上下文中的variableObject */ }, this: {} }
3>建立階段以及代碼執行階段的詳細分析
確 切地說,執行上下文對象(上述的executionContextObj)是在函數被調用時,但是在函數體被真正執行以前所創建的。函數被調用時,就是我 上述所描述的兩個階段中的第一個階段 - 建立階段。這個時刻,引擎會檢查函數中的參數,聲明的變量以及內部函數,然后基于這些信息建立執行上下文對象 (executionContextObj)。在這個階段,variableObject對象,作用域鏈,以及this所指向的對象都會被確定。
上述第一個階段的具體過程如下:
1.找到當前上下文中的調用函數的代碼 2.在執行被調用的函數體中的代碼以前,開始創建執行上下文 3.進入第一個階段-建立階段: 建立variableObject對象: 建立arguments對象,檢查當前上下文中的參數,建立該對象下的屬性以及屬性值 檢查當前上下文中的函數聲明: 每找到一個函數聲明,就在variableObject下面用函數名建立一個屬性,屬性值就是指向該函數在內存中的地址的一個引用 如果上述函數名已經存在于variableObject下,那么對應的屬性值會被新的引用所覆蓋。 檢查當前上下文中的變量聲明: 每找到一個變量的聲明,就在variableObject下,用變量名建立一個屬性,屬性值為undefined。 如果該變量名已經存在于variableObject屬性中,直接跳過(防止指向函數的屬性的值被變量屬性覆蓋為undefined),原屬性值不會被修改。 初始化作用域鏈 確定上下文中this的指向對象 4.代碼執行階段: 執行函數體中的代碼,一行一行地運行代碼,給variableObject中的變量屬性賦值。 下面來看個具體的代碼示例: function foo(i) { var a = "hello"; var b = function privateB() { }; function c() { } } foo(22); 在調用foo(22)的時候,建立階段如下: fooExecutionContext = { variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: undefined, b: undefined }, scopeChain: { ... }, this: { ... } } 由此可見,在建立階段,除了arguments,函數的聲明,以及參數被賦予了具體的屬性值,其它的變量屬性默認的都是undefined。一旦上述建立階段結束,引擎就會進入代碼執行階段,這個階段完成后,上述執行上下文對象如下: fooExecutionContext = { variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: "hello", b: pointer to function privateB() }, scopeChain: { ... }, this: { ... } } 我們看到,只有在代碼執行階段,變量屬性才會被賦予具體的值。
4>局部變量作用域提升的緣由
在網上一直看到這樣的總結: 在函數中聲明的變量以及函數,其作用域提升到函數頂部,換句話說,就是一進入函數體,就可以訪問到其中聲明的變量以及函數。這是對的,但是知道其中的緣由嗎?相信你通過上述的解釋應該也有所明白了。不過在這邊再分析一下。看下面一段代碼:
function() { console.log(typeof foo); // function pointer console.log(typeof bar); // undefined var foo = "hello", bar = function() { return "world"; }; function foo() { return "hello"; } }()); 上述代碼定義了一個匿名函數,并且通過()運算符強制理解執行。那么我們知道這個時候就會有個執行上下文被創建,我們看到例子中馬上可以訪問foo以及bar變量,并且通過typeof輸出foo為一個函數引用,bar為undefined。 為什么我們可以在聲明foo變量以前就可以訪問到foo呢?
因 為在上下文的建立階段,先是處理arguments, 參數,接著是函數的聲明,最后是變量的聲明。那么,發現foo函數的聲明后,就會在variableObject下面建立一個foo屬性,其值是一個指向 函數的引用。當處理變量聲明的時候,發現有var foo的聲明,但是variableObject已經具有了foo屬性,所以直接跳過。當進入代碼執行階段的時候,就可以通過訪問到foo屬性了,因為它 已經就存在,并且是一個函數引用。
為什么bar是undefined呢?
因為bar是變量的聲明,在建立階段的時候,被賦予的默認的值為undefined。由于它只要在代碼執行階段才會被賦予具體的值,所以,當調用typeof(bar)的時候輸出的值為undefined。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83649.html
摘要:當被創建時,它的作用域鏈初始化為當前運行函數的屬性中的對象,這些值按照他們出現在函數中的順序,被復制到執行環境的作用域鏈中。然后這個對象被推入作用域鏈最前端。 在計算機科學中,數據存儲的位置關系到代碼執行過程中數據的檢索速度,有一個經典的問題即為:通過改變數據的存儲位置來獲得最佳的讀寫性能。 Javascript中四種基本的數據存儲位置 字面量字面量只代表自身,不存儲在特定的位置。...
摘要:為什么會這樣這段代碼究竟是如何運行的執行上下文堆棧瀏覽器中的解釋器單線程運行。瀏覽器始終執行位于堆棧頂部的,并且一旦函數完成執行當前操作,它將從堆棧頂部彈出,將控制權返回到當前堆棧中的下方上下文。確定在上下文中的值。 原文:What is the Execution Context & Stack in JavaScript? git地址:JavaScript中的執行上下文和隊列(...
摘要:當遇到函數調用時,引擎為該函數創建一個新的執行上下文并把它壓入當前執行棧的頂部。參考鏈接理解中的執行上下文和執行棧深入之執行上下文棧 開篇 作為一個JavaScript的程序開發者,如果被問到JavaScript代碼的執行順序,你腦海中是不是有一個直觀的印象 -- JavaScript 是順序執行的,可事實真的是這樣的嗎? 讓我們首先看兩個小例子: var foo = functio...
摘要:講作用域鏈首先要從作用域講起,下面是百度百科里對作用域的定義作用域在許多程序設計語言中非常重要。原文出處談談語法里一些難點問題二 3) 作用域鏈相關的問題 作用域鏈是javascript語言里非常紅的概念,很多學習和使用javascript語言的程序員都知道作用域鏈是理解javascript里很重要的一些概念的關鍵,這些概念包括this指針,閉包等等,它非常紅的另一個重要原因就...
閱讀 2926·2021-11-24 09:39
閱讀 3609·2021-11-22 13:54
閱讀 3414·2021-11-16 11:45
閱讀 2439·2021-09-09 09:33
閱讀 3199·2019-08-30 15:55
閱讀 1296·2019-08-29 15:40
閱讀 924·2019-08-29 15:19
閱讀 3400·2019-08-29 15:14