摘要:邏輯上講,活躍的執行上下文集合組成了一個棧。棧底是全局上下文,棧頂是當前活躍執行上下文。舉個例子,我們將執行上下文定義成一個數組。一個拋出但是沒有捕獲的異常可能導致退出一個或多個執行上下文代碼代碼更加復雜。
原文地址
作者的話有很多文章已經對ECMAScript的核心概念做了詳盡解讀。本系列文章翻譯自Dmitry Soshnikov的個人網站,相信不少人已經看過原文或者譯文。原文簡潔易懂并且嚴謹,條理清晰地闡明了所有JavaScript開發者不得不深入理解的ECMAScript核心概念。重復翻譯的原因主要是為了個人收藏、整理之用。初次翻譯,技巧拙劣,如有不足,請不吝賜教。
正文介紹
定義
可執行代碼的類型
全局代碼
函數代碼
Eval代碼
結論
介紹在這篇文章中我們將提到ECMAScript的執行上下文和與之相關的可執行代碼類型。
定義每次當控制轉移到ECMAScript可執行的代碼,控制就進入了一個執行上下文。
執行上下文(Execution context,縮寫:EC)是ECMA-262標準使用的抽象概念,用來分類和區別一段可執行代碼
標準沒有從技術實現角度定義EC的準確結構和類型,這是ECMAScript引擎如何實現標準的問題。
邏輯上講,活躍的執行上下文集合組成了一個棧。棧底是全局上下文(global context),棧頂是當前(活躍)執行上下文。在進入和退出不同的EC時,棧被修改(pop/push)。
可執行代碼的類型的概念與執行上下文的抽象概念相關。講到代碼類型,在特定時候,它可以指執行上下文。
舉個例子,我們將執行上下文定義成一個數組。
ECStack = [];
每次進入一個函數,棧會被壓入一個上下文(即使是函數被遞歸調用,或者作為構造函數被調用),在eval函數中也是如此。
全局代碼這個類型的代碼在程序(Program)層面被處理:比如加載的外部js文件、內聯代碼(在標簽內)。全局代碼不包含任何函數體內的代碼。
在初始化(程序開始)時,ECStack看起來像這樣:
ECStack = [ globalContext ];函數代碼
在進入函數代碼(所有類型的函數)時,ECStack會被塞入新的元素。需要注意到:所說的函數代碼不包含內部函數的代碼。
舉個例子,我們看看這個遞歸調用自身一次的函數:
(function foo(flag) { if (flag) { return; } foo(true); })(false);
然后,ECStack被修改成下面這樣:
// first activation of foo ECStack = [functionContext globalContext ]; // recursive activation of foo ECStack = [ functionContext – recursively functionContext globalContext ];
每次函數返回會退出當前執行上下文,ECStack彈出一個元素。當這段代碼的工作結束,ECStack再一次地僅包含globalContext-直到程序退出。
一個拋出但是沒有捕獲的異常可能導致退出一個或多個執行上下文:
(function foo() { (function bar() { throw "Exit from bar and foo contexts"; })(); })();Eval代碼
eval代碼更加復雜。在這種情況下,有一個概念叫調用上下文(calling context),比如eval函數被調用的地方的上下文:
// influence global context eval("var x = 10"); (function foo() { // and here, variable "y" is // created in the local context // of "foo" function eval("var y = 20"); })(); alert(x); // 10 alert(y); // "y" is not defined
注意,在ES5的嚴格模式中,eval已經不會影響 調用上下文,而是在本地沙箱中運行代碼。
對于上面的例子,我們有如下的ECStack修改:
ECStack = [ globalContext ]; // eval("var x = 10"); ECStack.push({ context: evalContext, callingContext: globalContext }); // eval exited context ECStack.pop(); // foo funciton call ECStack.push(functionContext); // eval("var y = 20"); ECStack.push({ context: evalContext, callingContext: functionContext }); // return from eval ECStack.pop(); // return from foo ECStack.pop();
非常隨意而正常的調用棧。
在老版的SpiderMonkey 實現(firefox)中,最多到1.7版本,可以將 調用上下文作為第二個參數傳給eval函數。因此,如果上下文任存在,會影響到私有變量:
function foo() { var x = 1; return function () { alert(x); }; }; var bar = foo(); bar(); // 1 eval("x = 2", bar); // pass context, influence internal var "x" bar(); // 2
然而,由于現代引擎的安全問題,它被修復而不在有意義。
ES2015+引入了一種新的代碼類型-模塊代碼
結論這些基礎的理論需要被用來更深入地研究與執行上下文相關的細節,比如變量對象和作用域鏈,這些描述可以在適當的章節被找到。
原始作者:Dmitry Soshnikov
原始發布時間:2009-06-26
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96823.html
摘要:不同執行上下文中的變量對象對于所有的執行上下文類型,變量對象的一些操作比如變量實例化和行為是共通的。從這點看,將變量對象看作是抽象的基礎的東西很便利。全局上下文的變量對象現在,應該先給出全局對象的定義。 原文地址 作者的話 有很多文章已經對ECMAScript的核心概念做了詳盡解讀。本系列文章翻譯自Dmitry Soshnikov的個人網站,相信不少人已經看過原文或者譯文。原文簡潔易懂...
摘要:堆棧結構的底部是全局執行上下文,頂部是當前執行上下文。不同的執行上下文切換時堆棧會發生改變譯論及代碼類型時,在某些時候可能也意味著執行上下文。函數體中代碼執行完后,只剩全局上下文直到程序結束譯代碼更有意思。 第一次翻譯,希望各位多多包涵,有錯誤處還望指出,歡迎提出建議。 Chapter 1.Execution Contexts Introduction (介紹) Definitio...
摘要:如果在全局代碼中調用函數,程序的順序流進入被調用的函數,創建新的執行上下文并將其推送到執行堆棧的頂部。每次調用函數時,都會創建一個新的執行上下文。 翻譯:瘋狂的技術宅鏈接:http://davidshariff.com/blog/... 本文首發微信公眾號:jingchengyideng歡迎關注,每天都給你推送新鮮的前端技術文章 在這篇文章中,我將深入探討JavaScript的最...
摘要:前言大家學的時候,經常遇到自執行匿名函數的代碼,今天我們主要就來想想說一下自執行。其實,前面兩個例子里的變量,也可以換成,因為和外面的不在一個作用于,所以不會出現問題,這也是匿名函數閉包的威力。 前言 大家學JavaScript的時候,經常遇到自執行匿名函數的代碼,今天我們主要就來想想說一下自執行。 在詳細了解這個之前,我們來談了解一下自執行這個叫法,本文對這個功能的叫法也不一定完全對...
摘要:然后最后一步就是從父作用域鏈中將該特殊對象刪除,整個過程的偽代碼如下注意這里,該屬性不能刪除,只讀。 起因是我在逛sf的時候看到了一個人的提問: 為什么將函數c賦值給變量b,在函數體里面,給c賦值,為什么會失???也就是這代碼執行時為什么c打印出來的不是3 var b = function c () { a=1, b=2, c=3; console.log(a); ...
閱讀 1625·2021-11-02 14:42
閱讀 521·2021-10-18 13:24
閱讀 939·2021-10-12 10:12
閱讀 1817·2021-09-02 15:41
閱讀 3201·2019-08-30 15:56
閱讀 2874·2019-08-29 16:09
閱讀 2056·2019-08-29 11:13
閱讀 3617·2019-08-28 18:06