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

全國(guó)咨詢(xún)/投訴熱線(xiàn):400-618-4000

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

更新時(shí)間:2018年08月02日14時(shí)46分 來(lái)源:傳智播客 瀏覽次數(shù):

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

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

函數(shù)代碼
當(dāng)進(jìn)入funtion函數(shù)代碼(所有類(lèi)型的funtions)的時(shí)候,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的時(shí)候,都會(huì)退出當(dāng)前執(zhí)行上下文的,相應(yīng)地ECStack就會(huì)彈出,棧指針會(huì)自動(dòng)移動(dòng)位置,這是一個(gè)典型的堆棧實(shí)現(xiàn)方式。一個(gè)拋出的異常如果沒(méi)被截獲的話(huà)也有可能從一個(gè)或多個(gè)執(zhí)行上下文退出。相關(guān)代碼執(zhí)行完以后,ECStack只會(huì)包含全局上下文(global context),一直到整個(gè)應(yīng)用程序結(jié)束。
Eval 代碼
eval 代碼有點(diǎn)兒意思。它有一個(gè)概念: 調(diào)用上下文(calling context),例如,eval函數(shù)調(diào)用的時(shí)候產(chǎn)生的上下文。eval(變量或函數(shù)聲明)活動(dòng)會(huì)影響調(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" 提示沒(méi)有聲明
ECStack的變化過(guò)程:
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();
也就是一個(gè)非常普通的邏輯調(diào)用堆棧。
在版本號(hào)1.7以上的SpiderMonkey(內(nèi)置于Firefox,Thunderbird)的實(shí)現(xiàn)中,可以把調(diào)用上下文作為第二個(gè)參數(shù)傳遞給eval。那么,如果這個(gè)上下文存在,就有可能影響“私有”(有人喜歡這樣叫它)變量。
[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



作者:傳智播客前端與移動(dòng)開(kāi)發(fā)培訓(xùn)學(xué)院
首發(fā):http://web.itcast.cn/
0 分享到:
和我們?cè)诰€(xiàn)交談!