JS 中 TDZ 的理解

原文連接:https://acrens.github.io/2017/01/22/2017-01-22-TDZ/
春節快到了,假期也快到了,空閒之餘刷個微博,看見 @ruanyf 提出了一個問題與 TDZ 有關,可是貌似阮大當時尚未意識到這個問題,多虧一些其餘業內同仁提出了與 TDZ 相關;固然,以阮大的能力這都不是事。因爲當時我自己也還不知道 TDZ 這一回事沒有看懂,因此就花了一些時間去搞清楚什麼是 TDZ 及TDZ會帶來一些什麼問題,本文主要是用於介紹我對 TDZ 的一些理解,若有問題,多謝指出。javascript

示例

  • 案例一java

    1. 代碼git

    let y = 1;
    function foo(x = y, y) {
        console.log(x);
    }
    foo();  // ReferenceError: y is not defined
    1. 解讀es6

      • 當函數存在默認參數時,且調用方法不傳任何參數,會存在三個做用域環境;github

      • 全局做用域、參數做用域、函數體做用域;ecmascript

      • 當執行 foo 函數時,參數做用域在 x = y 以後才定義 let y,注意:let 定義,因此根據 let 定義變量的做用知道 x = y 確定會報錯;函數

    2. 代碼翻譯:將以上代碼翻譯以後能夠按下面代碼片斷閱讀更易於理解測試

    function analysis() {
        "use strict";
        let y = 1;
    
        function foo() {  
            let x = arguments[0] !== (void 0) ? arguments[0] : y;   // y not defined
            let y = arguments[1];
        }
        foo();
    
        return {};
    }
  • 案例二google

    1. 代碼翻譯

    let y = 1;
    function foo(x = function(){console.log(y)}, y = 2) {
        x(); // 2
        y = 3;
        x(); // 3
    }
    foo();
    console.log(y); //1
    1. 解讀

      • 當函數存在默認參數時,且調用方法不傳任何參數,會存在三個做用域環境;

      • 全局做用域、參數做用域、函數體做用域;

      • 當執行 foo 函數時,x 被申明爲匿名函數變量,此時函數並未被執行,因此正常;以後定義 y 值爲 2,此時調用 x() 輸出的固然是變量 y 的值,以後繼續修改 y 的值,再繼續調用 x(),輸出 y 最新值 3;當執行外部 console.log(y) 時並不能訪問內部函數變量,訪問的變量是當前域下的 y = 1 的值 1,因此輸出 1;

    2. 代碼翻譯:將以上代碼翻譯以後能夠按下面代碼片斷閱讀更易於理解

    function analysis() {
        "use strict";
        let y = 1;
    
        function foo() {  
            let x = arguments[0] !== (void 0) ? arguments[0] : function() {
                console.log(y);
            };
            let y = arguments[1] !== (void 0) ? arguments[1] : 2;
            x(); // 2
            y = 3;
            x();    // 3
        }
        foo();
        console.log(y); // 1
    
        return {};
    }
  • 案例三

    1. 代碼

    let y = 1;
    function foo(x = function(){console.log(y)}) {
        let y = 3;
        x(); // 1
    }
    foo();
    1. 解讀

      • 當函數存在默認參數時,且調用方法不傳任何參數,會存在三個做用域環境;

      • 全局做用域、參數做用域、函數體做用域;

      • 當執行 foo 函數時,x 被賦值爲一個匿名函數的變量,且存在與參數做用域內,let y = 3 會被定義到函數體做用域內,屬於參數做用域的內部函數;當 x() 執行時是在函數體做用域定被調用,可是其定義是在參數做用域,因此執行環境是在參數做用域內,此時在參數做用域沒有定義 y 變量,也不能訪問內部函數 funBody 內部定義的變量 y,此時往上級函數查找是否存在 y 被定義,若是被定義則輸出其值,因此輸出最外層變量 y 的值 1;

    2. 代碼翻譯:將以上代碼翻譯以後能夠按下面代碼片斷閱讀更易於理解

    function analysis() {
        "use strict";
        let y = 1;
    
        function foo() {  
            let x = arguments[0] !== (void 0) ? arguments[0] : function() {
                console.log(y);
            };
    
            function funBody() {
                let y = 3;
                x();
            }
            funBody();
        }
        foo();
    
        return {};
    }
  • 案例四

    1. 代碼

    function foo(x = function(){console.log(y)}) {
        let y = 3;
        x(); // // ReferenceError: y is not defined
    }
    foo();
    1. 解讀

      • 當函數存在默認參數時,且調用方法不傳任何參數,會存在三個做用域環境;

      • 全局做用域、參數做用域、函數體做用域;

      • 當執行 foo 函數時,x 被賦值爲一個匿名函數的變量,且存在與參數做用域內,let y = 3 會被定義到函數體做用域內,屬於參數做用域的內部函數;當 x() 執行時是在函數體做用域定被調用,可是其定義是在參數做用域,因此執行環境是在參數做用域內,此時在參數做用域沒有定義 y 變量,也不能訪問內部函數 funBody 內部定義的變量 y,此時往上級函數查找是否存在 y 被定義,若是被定義則輸出其值,不然報 y 沒有被定義錯誤,此案例只是案例三的一種測試;

    2. 代碼翻譯:將以上代碼翻譯以後能夠按下面代碼片斷閱讀更易於理解

    function analysis() {
        "use strict";
        function foo() {  
            let x = arguments[0] !== (void 0) ? arguments[0] : function() {
                console.log(y);
            };
    
            function funBody() {
                let y = 3;
                x();
            }
            funBody();
        }
        foo();
    
        return {};
    }

參考

以上核心部分在代碼翻譯部分,經過配合一下資料及我的的理解,翻譯出通俗易懂的代碼:

相關文章
相關標籤/搜索