詞法階段
詞法作用域是由你在寫代碼時將變量和塊作用域?qū)懺谀睦飦頉Q定的,因此當(dāng)詞法分析器處理代碼時會保持作用域不變(大部分情況下是這樣的)。
嵌套作用域
function foo(a) {
var b = a * 2;
function bar(c) {
console.log( a, b, c );
}
bar( b * 3 );
}
foo( 2 ); // 2, 4, 12
標(biāo)識符 foo() 作用域
標(biāo)識符 foo(a){} 作用域
標(biāo)識符 bar(c) 作用域
欺騙詞法
eval () 函數(shù)可以接受一個字符串為參數(shù),并將其中的內(nèi)容視為好像在書寫時就存在于程序中這個位置的代碼。
setTimeout(..) 和 setInterval(..) 的第一個參數(shù)可以是字符串,字符串的內(nèi)容可以被解釋為一段動態(tài)生成的函數(shù)代碼。
new Function(..) 函數(shù)的行為也很類似,最后一個參數(shù)可以接受代碼字符串,并將其轉(zhuǎn)化為動態(tài)生成的函數(shù)(前面的參數(shù)是這個新生成的函數(shù)的形參)。
function foo(str, a) {
eval( str ); // 欺騙!
console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 ); // 1, 3
with
with 通常被當(dāng)作重復(fù)引用同一個對象中的多個屬性的快捷方式,可以不需要重復(fù)引用對象本身。
with坑
function foo(obj) {
with (obj) {
a = 2;
}
}
var o2 = {
b: 3
};
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2——不好,a 被泄漏到全局作用域上了!
o2中沒有a 放入with中后 無法賦值 ?a=2就會變?yōu)槿肿兞?通過LHS來查找滴話。
性能
eval(..) 和 with 會在運(yùn)行時修改或創(chuàng)建新的作用域,以此來欺騙其他在書寫時定義的詞法作用域。
JavaScript 引擎會在編譯階段進(jìn)行數(shù)項的性能優(yōu)化。其中有些優(yōu)化依賴于能夠根據(jù)代碼的詞法進(jìn)行靜態(tài)分析,并預(yù)先確定所有變量和函數(shù)的定義位置,才能在執(zhí)行過程中快速找到標(biāo)識符。
最悲觀的情況是如果出現(xiàn)了 eval(..) 或 with,所有的優(yōu)化可能都是無意義的,因此最簡單的做法就是完全不做任何優(yōu)化。