Ch. 2 語彙範疇


語彙範疇:定義於編寫時期。JS 採用此種模型。

動態範疇:不在意宣告,而取決於從何處被呼叫。(附錄 A)

function foo() {
	console.log(a); // 語彙範疇:印出 2;動態範疇:印出 3。
}
function bar() {
	var a = 3;
	foo();
}
var a = 2;
bar();

語彙分析時期

語彙範疇是在語彙分析時期 (lexing) 定義的範疇。視變數與區塊在寫作時期被放置於何處來決定。語彙分析時期能知道所有識別字於何處宣告,以預測執行過程中會如何被查找。

  1. Bubble 1:foo
  2. Bubble 2:a, bar, b
  3. Bubble 3:c

查找

  • 遮蔽:相同識別字可能出現在不同的範疇中,但只要找到第一個就會停止動作。意即,內層的識別字會遮蔽其他外層識別字。
  • 全域變數:全域變數會自動成為全域物件的屬性 (e.g. window.a),因此可以拿來避免變數被遮蔽而取不到的問題。

查找動作永遠從當時執行的範疇開始,並往外或往上尋找,直到找到第一個符合的為止。

查找動作只適用於一級識別字,如 foo.bar.baz,只會查找 foo。後續交由物件屬性存取規則繼續解析。


語彙範疇作弊技巧

eval

eval 可以傳入一個字串作為參數,並將該字串內容視為程式原本已經編寫好的程式碼。

function foo(str, a) {
	eval(str); // var b = 3;
	console.log(a, b);
}
var b = 2;
foo('var b = 3;', 1); // 1 3

var b = 3; 被視為一直都在那裏的程式碼,b 因此遮蔽了外層全域的 b。

eval 能在執行時期修改編寫時期的語彙範疇。

with

with 接受一個物件,並把物件作為獨立的語彙範疇看待,將物件屬性視為該範疇下的識別字。

function foo(obj) {
	with (obj) {
		a = 2;
	}
}
var o2 = {
	b: 3
};
foo(o2);
console.log(o2.a); // undefined
console.log(a); // 2,全域值外漏

考慮以上程式碼,當在範疇中找不到 a 時,LHS 查找後自動產生全域變數 a。

with 能在執行時期將物件做為一個範疇來看待。

效能

eval 和 with 兩者在執行時期修改了編寫時期的語彙範疇;或是建立了新的語彙範疇。

Engine 在編譯時期會進行最佳化,事先確定所有變數和宣告的位置。然而若它發現了 eval 和 with,代表它原先做的靜態分析可能都是無效的,於是它乾脆放棄了最佳化,導致效能下降。


複習

  • 請說明語彙範疇。
  • eval 與 with 的缺點。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *