這次帶給大家頁面中執行上下文使用詳解,頁面中執行上下文的注意事項有哪些,下面就是實戰案例,一起來看一下。
當 JavaScript 程式碼執行一段可執行程式碼時,會建立對應的上下文(execution context)並將該上下文壓入上下文堆疊(context stack)中。
上下文包含以下3個重要屬性:
name | - | |
---|---|---|
##變數物件(VO, variable object) | 目前函數定義的變數、函數、參數 | |
作用域鏈(Scope chain) | 原始碼定義時形成的作用域鏈 | |
this |
上下文是一個抽象概念,為了方便理解我們假設上下文是一個物件並且包含VO、Scope、this這三個屬性:
function foo (c) { let a = 1 let b = function () {} } // foo函数的上下文 fooContext = { VO: { arguments: { // 实参 c: undefind, length: 0 }, a: 1, // 变量 b: reference to function (){} // 函数 }, Scope: [VO, globalContext.VO], // 作用域链 this: undefind // 非严格模式下为 this }
所以上下文是函數運行時的環境或者說是依賴資源的集合,它決定了函數在運行時可以取得哪些變數、函數。
執行上下文(EC): 如果函數處於正在執行狀態則函數的上下文稱為執行上下文, 同時如果函數處於非執行狀態則為(普通)上下文。所以執行上下文
只是上下文
的不同狀態,本質上它們沒有差別。
上下文堆疊又稱為執行堆疊(ECS), 瀏覽器中javascript 解析器本身是單線程的,即同一時間只能處理一個上下文及對應的程式碼段,所以javascript 解析引擎使用上下文堆疊來管理上下文。所有的上下文創建後會保存在上下文棧佇列裡。棧底為全域上下文,棧頂為目前正在執行的上下文。
一個上下文就是一個執行單元, javascript 以堆疊的方式管理執行單元。頁面初始化的時候首先會在堆疊底部壓入全域上下文,然後根據規則執行到可執行函數時會將函數的上下文壓入上下文堆疊
中, 被壓入的上下文包含有該函數運行時所需的資源(變數物件、作用域鏈、this),這些資源提供給函數執行時期的表達式使用。
執行上下文可以理解為函數運行時的環境。同時執行上下文也是一個不可見的概念。
javascript 中有3種運行環境:
全域環境:在瀏覽器中是 window
, 在node 環境中是 global
,當頁面初始化時會將全域上下文壓入上下文堆疊;
函數環境:當函數被呼叫執行時會收集該函數的資源,建立上下文並壓入上下文堆疊;
eval環境,棄用
一個運行環境會對應一個上下文。位於堆疊頂部的上下文執行完畢後會自動出棧,依序向下直至所有上下文運行完畢,最後瀏覽器關閉時全域上下文被銷毀。為了好理解來舉栗子:
let i = 0 function foo () { i++ console.log(i, 'foo') } function too () { i++ console.log(i, 'too') foo() } function don () { i++ console.log(i, 'don') too() } don() // 1 "don" // 2 "too" // 3 "foo"
上面程式碼的邏輯就是先執行don(),然後是too()、foo()。執行到foo()時的上下文堆疊是這樣的:
我們假設上下文堆疊為陣列:ECStack
:
ECStack = []
javascript 載入完成後首先解析執行的是全域程式碼,所以初始化的時候會向上下文堆疊中push 全域上下文,我們用globalContext
來表示。
ECStack = [ globalContext ]
全域作用域在整個程式碼運行階段會一直存在,直到頁面關閉時 ECStack
會被請空,從而globalContext
則被銷毀。
全域上下文建立的時候進行變數提升、產生變數物件等操作,而後會執行目前上下文中的可執行程式碼(函數、表達式)。遇到函數呼叫的時候會向上下文堆疊中push
該函數的上下文。
function foo () { console.log('foo') } function too () { console.log('too') foo() } function don () { too() } don()
執行邏輯可以理解為:
執行到don(), 解析don函數內部程式碼
##壓入too 的上下文到ECStack
...#javascript 解析器不斷遞歸直到foo 函數執行完... foo 函數上下文被彈出...然後回溯到
执行逻辑的伪代码如下:
// 伪代码 // don() ECStack.push(<don> functionContext); // 在don中调用了too, push too的上下文到上下文栈里 ECStack.push(<fun2> functionContext); // 在too中调用了foo, push foo的上下文到上下文栈里 ECStack.push(<fun3> functionContext); // foo执行完毕, 弹出上下文 ECStack.pop(); // too执行完毕, 弹出上下文 ECStack.pop(); // don执行完毕, 弹出上下文 ECStack.pop(); // 非全局上下文执行完毕被弹出后会一直停留在全局上下文里,直至页面关闭</fun3></fun2></don>
需要注意的是,上下文与作用域(scope)是不同的概念。上下文是一个运行时概念,浏览器运行后执行 js 代码,将不同的上下文加入上下文栈中,顶层的上下文对应的代码块执行完后又将该上下文销毁。 而作用域是一个静态概念,根据所在代码片段的位置及词法关系确立的,不管浏览器运行与否,源代码的作用域关系、变量的访问权限依然不变。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上是頁面中執行上下文使用詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!