前端-JavaScript作用域和執行分析

程式設計碼農發表於2021-10-30

作用域

JavaScript中的作用域分為全域性作用域函式作用域塊作用域

全域性作用域

對於全域性範圍內宣告的變數,可以在任何地方訪問。

var messge = 'Hello'
function say(){
  console.log(message)
}
say(); // Hello

函式作用域

在函式內宣告的變數,只能從該函式內部訪問。

function say() {
  var messge = 'Hello';
  console.log(messge);
}
say(); // Hello
console.log(greeting); // ReferenceError: messge is not defined

塊作用域

ES6 引入了letconst 關鍵字,它們宣告的變數只能從該程式碼塊內訪問。

{
  let messge = 'Hello';
  console.log(messge);
}
console.log(messge); // ReferenceError: messge is not defined

靜態作用域

在詞法分析時(編譯時)確定。

let number = 42;
function printNumber() {
  console.log(number);
}
function log() {
  let number = 54;
  printNumber();
}

log(); // 42

作用域鏈

JavaScript執行時,引擎首先會在當前範圍內查詢變數,如果找不到,會向父作用域查一層一層向上查詢,一直找到頂層全域性作用域,如果還是找不到就返回undefined

var g = 'Global hello'
function f1() {
    var g1 = 'G1 hello';
    function f2() {
        var g2 = 'G2 hello'
        function f3() {
            console.log(g, g1, g2)
        }
          f3()
    }
      f2();
}
f1(); // Global hello G1 hello G2 hello

作用域鏈

執行分析

執行上下文

執行上下文是執行一段 JavaScript 的環境,它儲存了執行程式碼的一些必要資訊。執行上下文分全域性執行上下文函式執行上下文

  • 全域性執行上下文,一個程式中只會有一個,函式之外的程式碼都在全域性執行上下文中執行。
  • 函式執行上下文,函式在每次呼叫時都會建立一個對應的函式執行上下文

執行上下文的包含變數環境Variable environment),作用域鏈Scope chain),this 指向。

  • 變數環境,函式內部所有的變數和物件引用和呼叫引數。
  • 作用域鏈,當前函式之外的變數的引用組成。
  • this 指向。

從JavaScript執行時的記憶體結構來看,呼叫棧就是儲存執行上下文的集合。

js記憶體

案例

案例一

這是一個普通的JavaScript例子,執行流程分析:

var msg = 'Global hello'

function say() {
    var msg = 'Hello'
      return msg;
}

var s = say();
console.log(message) // Hello
  1. 建立全域性執行上下文,然後入呼叫棧。
  2. 呼叫函式say(),建立say()函式執行上下文,併入呼叫棧。
  3. 執行完say()函式將結果返回,更新全域性執行上下文裡的s 變數。
  4. say 函式的執行上下文彈出棧。

1

2

案例二

這個例子跟上面不同的是返回值是函式,這個匿名函式也稱閉包,它訪問了函式外部的變數,即使外部函式執行上下文被彈出棧後,它依然可以持有外部變數的引用。執行分析如下:

var msg = 'Global hello'

function say() {
    var msg = 'Hello'
    return function() {
        console.log(msg)
    };
}

var s = say();
s()
  1. 建立全域性執行上下文,然後入呼叫棧。
  2. 呼叫函式say(),建立say()函式執行上下文,併入呼叫棧。
  3. 執行完say()函式將結果返回,更新全域性執行上下文裡的s 變數,s 是函式型別,它依然持有say var = msg 引用。
  4. say 函式的執行上下文彈出棧。
  5. 執行 s() ,建立s()函式執行上下文
  6. s() 函式的執行上下文彈出棧。

3

思考題

在腦海裡動態執行下面程式碼。

題目一:

var msg = 'Global hello'

function getMsgFunc() {
    var msg = 'Hello';
    function getMsg() {
        return msg;
    }
    return getMsg();
}
console.log(getMsgFunc());

題目二:

var msg = 'Global hello'

function getMsgFunc() {
    var msg = 'Hello';
    function getMsg() {
        return msg;
    }
    return getMsg;
}
console.log(getMsgFunc()());

題目三:

     var msg = 'Global hello'

  var obj = {
    msg : 'Hello',

    getMsgFunc : function(){
      return function(){
        return this.msg;
      };
    }
  };
  console.log(obj.getMsgFunc()());

題目四:

     var msg = 'Global hello'

  var obj = {
    msg : 'Hello',

    getMsgFunc : function(){
              var that = this
      return function(){
        return that.msg;
      };
    }
  };
  console.log(obj.getMsgFunc()());

相關文章