• <noscript id="ynq8u"></noscript>

    <s id="ynq8u"></s>
  • <strong id="ynq8u"></strong>

    <code id="ynq8u"><blockquote id="ynq8u"></blockquote></code>

    <small id="ynq8u"></small>
      1. 全國咨詢熱線:400-618-4000

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

        創建時間:2018年08月02日14時46分

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

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

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

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

        每次return的時候,都會退出當前執行上下文的,相應地ECStack就會彈出,棧指針會自動移動位置,這是一個典型的堆棧實現方式。一個拋出的異常如果沒被截獲的話也有可能從一個或多個執行上下文退出。相關代碼執行完以后,ECStack只會包含全局上下文(global context),一直到整個應用程序結束。
        Eval 代碼
        eval 代碼有點兒意思。它有一個概念: 調用上下文(calling context),例如,eval函數調用的時候產生的上下文。eval(變量或函數聲明)活動會影響調用上下文(calling context)。
        [JavaScript] 純文本查看 復制代碼
        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();
        也就是一個非常普通的邏輯調用堆棧。
        在版本號1.7以上的SpiderMonkey(內置于Firefox,Thunderbird)的實現中,可以把調用上下文作為第二個參數傳遞給eval。那么,如果這個上下文存在,就有可能影響“私有”(有人喜歡這樣叫它)變量。
        [JavaScript] 純文本查看 復制代碼
        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); // 傳入上下文,影響了內部的var x 變量
         
        bar(); // 2



        作者:傳智播客前端與移動開發培訓學院
        首發:http://web.itcast.cn/
        香蕉电影