教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

深入理解JavaScript系列:執(zhí)行上下文

更新時間:2018年09月21日17時19分 來源:傳智播客 瀏覽次數(shù):

定義
每次當(dāng)控制器轉(zhuǎn)到ECMAScript可執(zhí)行代碼的時候,即會進入到一個執(zhí)行上下文。執(zhí)行上下文(簡稱-EC)是ECMA-262標準里的一個抽象概念,用于同可執(zhí)行代碼(executable code)概念進行區(qū)分。
標準規(guī)范沒有從技術(shù)實現(xiàn)的角度定義EC的準確類型和結(jié)構(gòu),這應(yīng)該是具體實現(xiàn)ECMAScript引擎時要考慮的問題。
活動的執(zhí)行上下文組在邏輯上組成一個堆棧。堆棧底部永遠都是全局上下文(global context),而頂部就是當(dāng)前(活動的)執(zhí)行上下文。堆棧在EC類型進入和退出上下文的時候被修改(推入或彈出)。
可執(zhí)行代碼類型
可執(zhí)行代碼的類型這個概念與執(zhí)行上下文的抽象概念是有關(guān)系的。在某些時刻,可執(zhí)行代碼與執(zhí)行上下文完全有可能是等價的。
例如,我們可以定義執(zhí)行上下文堆棧是一個數(shù)組:
[JavaScript] 純文本查看 復(fù)制代碼
1
ECStack = [];

每次進入function (即使function被遞歸調(diào)用或作為構(gòu)造函數(shù)) 的時候或者內(nèi)置的eval函數(shù)工作的時候,這個堆棧都會被壓入。
全局代碼
這種類型的代碼是在"程序"級處理的:例如加載外部的js文件或者本地<script></script>標簽內(nèi)的代碼。全局代碼不包括任何function體內(nèi)的代碼。
在初始化(程序啟動)階段,ECStack是這樣的:
[JavaScript] 純文本查看 復(fù)制代碼
1
2
3
ECStack = [
  globalContext
];

函數(shù)代碼
當(dāng)進入funtion函數(shù)代碼(所有類型的funtions)的時候,ECStack被壓入新元素。需要注意的是,具體的函數(shù)代碼不包括內(nèi)部函數(shù)(inner functions)代碼。如下所示,我們使函數(shù)自己調(diào)自己的方式遞歸一次:
[JavaScript] 純文本查看 復(fù)制代碼
1
2
3
4
5
6
(function  foo(bar) {
  if (bar) {
    return;
  }
  foo(true);
})();

那么,ECStack以如下方式被改變:
[JavaScript] 純文本查看 復(fù)制代碼
01
02
03
04
05
06
07
08
09
10
11
12
// 第一次foo的激活調(diào)用
ECStack = [
  <foo> functionContext
  globalContext
];
// foo的遞歸激活調(diào)用
ECStack = [
  <foo> functionContext – recursively
  <foo> functionContext
  globalContext
];

每次return的時候,都會退出當(dāng)前執(zhí)行上下文的,相應(yīng)地ECStack就會彈出,棧指針會自動移動位置,這是一個典型的堆棧實現(xiàn)方式。一個拋出的異常如果沒被截獲的話也有可能從一個或多個執(zhí)行上下文退出。相關(guān)代碼執(zhí)行完以后,ECStack只會包含全局上下文(global context),一直到整個應(yīng)用程序結(jié)束。
Eval 代碼
eval 代碼有點兒意思。它有一個概念: 調(diào)用上下文(calling context),例如,eval函數(shù)調(diào)用的時候產(chǎn)生的上下文。eval(變量或函數(shù)聲明)活動會影響調(diào)用上下文(calling context)。
[JavaScript] 純文本查看 復(fù)制代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
eval('var x = 10');
(function foo() {
  eval('var y = 20');
})();
alert(x); // 10
alert(y); // "y" 提示沒有聲明
ECStack的變化過程:
ECStack = [
  globalContext
];
// eval('var x = 10');
ECStack.push(
  evalContext,
  callingContext: globalContext
);
// eval exited context
ECStack.pop();
// foo funciton call
ECStack.push(<foo> functionContext);
// eval('var y = 20');
ECStack.push(
  evalContext,
  callingContext: <foo> functionContext
);
// return from eval
ECStack.pop();
// return from foo
ECStack.pop();
也就是一個非常普通的邏輯調(diào)用堆棧。
在版本號1.7以上的SpiderMonkey(內(nèi)置于Firefox,Thunderbird)的實現(xiàn)中,可以把調(diào)用上下文作為第二個參數(shù)傳遞給eval。那么,如果這個上下文存在,就有可能影響“私有”(有人喜歡這樣叫它)變量。
[JavaScript] 純文本查看 復(fù)制代碼
01
02
03
04
05
06
07
08
09
10
11
12
function foo() {
  var x = 1;
  return function () { alert(x); };
};
 
var bar = foo();
 
bar(); // 1
 
eval('x = 2', bar); // 傳入上下文,影響了內(nèi)部的var x 變量
 
bar(); // 2

作者:傳智播客前端與移動開發(fā)培訓(xùn)學(xué)院
首發(fā):http://web.itcast.cn/
0 分享到:
和我們在線交談!