詞法作用域
作用域查找會在找到第一個匹配的標(biāo)識符時停止安吁,內(nèi)部標(biāo)識符會屏蔽同名外部標(biāo)識符。
-
全局變量會自動成為全局對象(比如window或global)睛琳,但這里有點小小的區(qū)別:
var a=1; b=2; function(){ var c=3; d=4; }
若上面代碼在瀏覽器中盒蟆,全局對象是
window
,a师骗,b历等,d都會成為window
的屬性,即均可以通過window.a
訪問(非嚴(yán)格模式下辟癌,嚴(yán)格模式下不通過var聲明會報錯)寒屯。
若上面的代碼在vscode中,全局對象是global
,只有b寡夹,d會成為global
的屬性(非嚴(yán)格模式下处面,嚴(yán)格模式下不通過var聲明會報錯),global
不會自動把用var聲明的變量添加為自己的屬性菩掏。
欺騙詞法
詞法作用域完全由寫代碼期間函數(shù)所聲明的位置來定義魂角,通過欺騙詞法可以在運行時修改詞法作用域,但是會導(dǎo)致性能下降智绸。
-
eval(...)
函數(shù)(在嚴(yán)格模式或ES6中被限制野揪,不建議使用)
接受一個字符串參數(shù),可以在你寫的代碼中用程序生成代碼并運行瞧栗,就好像代碼是寫在那個位置一樣斯稳。如:function foo(str,a){ eval(str); console.log(a,b); } var b=2; foo("var b=3;",1);//1 3
eval(...)
通常用來執(zhí)行動態(tài)創(chuàng)建的代碼。 -
with
關(guān)鍵字(在嚴(yán)格模式或ES6中被禁止使用)
with通常被當(dāng)作重復(fù)引用同一個對象中的多個屬性的快捷方式迹恐,可以不需要重復(fù)引用對象本身挣惰。function foo(obj){ with(obj){ a=3; } } var o1={a:2}; var o2={b:2}; foo(o1); foo(o2); console.log(o1.a);//3 console.log(o2.a);//undefined console.log(a);//3
在上面的代碼中,
with
會在代碼執(zhí)行時動態(tài)地延長作用域鏈(另外一個延長作用域鏈的方法是try-catch
語句塊中的catch
塊)系草,因為o2沒有a屬性通熄,所以通過LHS查詢,引擎在全局環(huán)境創(chuàng)建了一個變量a并將3賦值給a找都,所以o2.a為undefined唇辨。這里有個要注意的問題就是,訪問變量和訪問對象的屬性是不一樣的:
- 訪問變量時會LHS查詢和RHS查詢能耻,在嵌套的作用域鏈中查詢赏枚。
賦值或者取值分別按照第一章所說的規(guī)則進行LHS查詢和RHS查詢 - 訪問對象的屬性則是通過對象的
[[Put]]
操作和[[Get]]
操作來查找,在整個原型鏈([[prototype]]
鏈)查找
賦值時晓猛,通過[[Put]]
操作饿幅,若變量存在則將新的值賦值給變量(實際上情況很復(fù)雜,詳見第二部分第2章和第5章戒职,p.117)栗恩,若不存在,則為該對象創(chuàng)建該屬性變量并賦值洪燥;取值時磕秤,通過[[Get]]
操作取,若沒有找到則遍歷原型鏈捧韵,還是沒有找到則返回undefined市咆。
所以上面代碼改成下面的就會輸出不同結(jié)果:
function foo(obj){ obj.a=3; } var o1={a:2}; var o2={b:2}; foo(o1); foo(o2); console.log(o1.a);//3 console.log(o2.a);//3 console.log(a);//ReferenceError: a is not defined(即a未聲明undeclared)
- 訪問變量時會LHS查詢和RHS查詢能耻,在嵌套的作用域鏈中查詢赏枚。