最近在作編譯器時遇到靜態做用域和動態做用域的抉擇問題。
以前沒有仔細探究,作的時候才發現問題。javascript
靜態做用域,在編譯階段就作完全部name check的工做,像C語言:java
{ // Push scope 1 table int a = 10; // Insert (a, 10) to scope 1 { // Push scope 2 table int a = 15; // Insert (a, 15) to scope 2 int b = 20; // Insert (b, 20) to scope 2 a = 100; // Use a variable // Pop scope 2 table } // Pop scope 2 table }
Scope 1 | |
a | 10 |
Scope 2 | |
a | 15 |
b | 20 |
在語義分析階段的時候進行top-down parse,每進入一個block就push一張符號表,離開block時候就pop一張符號表,
使用一個變量時就依次從最後一個符號表向前找,若是找不到就該報undefined或者undeclared錯誤了。因此C語言的函數不能在定義前調用。由於在解析main的函數體時hello還沒插入到符號表裏。ide
int main(){ hello(); // Use of undeclared identifier "hello" return 0; } void hello(){};
動態做用域,像javascript這樣,就能夠在函數定義以前調用函數
var a = function(){ b(); // Run-time name check } var b = function(){} a();
a()->global
由於這時候再也不是編譯時的name check而是運行時的name check,做用域是函數做用域,在運行a函數而且執行到b()的時候,會在當前函數做用域找b的值,若是找不到就往上一個函數做用域尋找,直到global做用域。也就說,只要保證在函數運行到這個位置的時候,在做用域鏈裏存在這個變量,而與定義順序無關。spa