天天看點

JavaScript靜态作用域與動态作用域

概述

在文章最開始,先學習幾個概念:

  • 作用域:

    《你不知道的js》

    中指出,作用域是一套規則,這套規則用來管理引擎如何在目前作用域以及嵌套的子作用域中根據辨別符名稱進行變量查找。簡單來說,作用域規定了如何查找變量。
  • 靜态作用域:又稱詞法作用域,是指作用域在此法階段就被确定了,不會改變。
  • 動态作用域:函數的作用域在函數調用時才決定的。

靜态作用域與動态作用域

JavaScript

采用的是詞法作用域,函數定義的位置就決定了函數的作用域。

具體看一個例子:

var val = 1;
function test() {
    console.log(val);
}
function bar() {
    var val = 2;
    test();
}

bar();
// 結果是???
           

最終的輸出結果是

1

,說明

test

列印的是全局下的

val

,這也印證了

JavaScript

使用了靜态作用域。

在學習詞法作用域之前,已經學習了作用域鍊和預編譯過程,雖然有幾分本末倒置,但是對詞法的了解還是有一定幫助。

靜态作用域執行過程

當函數執行

test

函數時,先從内部的

AO

對象查找是否有

val

對象,如果沒有,沿着作用域鍊往上查找(由于

JavaScript

是詞法作用域),上層為全局

GO

,是以結果列印

1

動态作用域執行過程

但如果

JavaScript

采用的動态作用域,執行

test

函數,從函數内部查詢

val

變量,如果沒有,就調用函數的作用域,即

bar

函數的作用域,成功查詢到

val=2

思考題

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
           
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();
           

很明顯,兩個都列印

local scope

JavaScript

采用詞法作用域,上述兩個函數都定義在

checkscope

中,是以當

f()

函數執行時,首先查詢自身作用域是否有

scope

變量,如果沒有,它們的上級作用域都是

checkscope

,是以輸出

local scope

對此

《JavaScript權威指南》

解釋到:

JavaScript

函數的執行用到了作用域鍊,這個作用域鍊是在函數定義的時候建立的。嵌套的函數

f()

定義在這個作用域鍊裡,其中的變量

scope

一定是局部變量,不管何時何地執行函數

f()

,這種綁定在執行

f()

時依然有效。

如果想更深入、更透徹的了解上面的知識,建議深入學習作用域鍊和預編譯的知識。

參考部落格:JavaScript深入之詞法作用域和動态作用域

繼續閱讀