你不知道的JavaScript(二)|作用域和閉包

詞法階段
簡單地說,詞法作用域就是定義在詞法階段的作用域。換句話說,詞法作用域是由你在寫代碼時將變量和塊作用域?qū)懺谀睦飦頉Q定的,因此當(dāng)詞法分析器處理代碼時會保持作用域不變(大部分情況下是這樣的)。


以上,[1]包含著整個全局作用域,其中只有一個標(biāo)示符:foo。[2]包含著foo所創(chuàng)建的作用域,其中有三個標(biāo)示符:a、bar、b。[3]包含著bar所創(chuàng)建的作用域,其中只有一個標(biāo)示符:c。
注意,以上的氣泡是嚴(yán)格包含的。沒有任何函數(shù)的氣泡可以(部分地)同時出現(xiàn)在兩個外部作用域的氣泡中,就如同沒有任何函數(shù)可以部分地同時出現(xiàn)在兩個父級函數(shù)中一樣。
作用域查找會在找到第一個匹配的標(biāo)示符時停止。在多層的嵌套作用域中可以定義同名的標(biāo)示符,這叫做“遮蔽效應(yīng)”(內(nèi)部的標(biāo)示符“遮蔽”了外部的標(biāo)示符)。拋開遮蔽效應(yīng),作用域查找始終從運行時所處的最內(nèi)部作用域開始,逐級向外或者說向上進(jìn)行,直到遇見第一個匹配的標(biāo)示符為止。

欺騙詞法
如果詞法作用域完全由寫代碼期間函數(shù)所聲明的位置來定義,怎樣才能在運行時來“修改”(也可以說欺騙)詞法作用域呢?
欺騙詞法作用域會導(dǎo)致性能下降。
【方法一:eval】

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

但在嚴(yán)格模式的程序中,eval(..)在運行時有其自己的詞法作用域,意味著其中的聲明無法修改所在的作用域。

function foo(str) {
  "use strict";
  eval( str );
  console.log( a ); // ReferenceError: a is not defined
}
foo( "var a = 2" );

【方法二:with】
可以有很多方法來解釋with,在這里選擇從這個角度來解釋它:它如何同被它所影響的詞法作用域進(jìn)行交互。
with通常被當(dāng)做重復(fù)引用同一個對象中的多個屬性的快捷方式,可以不需要重讀引用對象本身。如:

var obj = {
  a: 1,
  b: 2,
  c: 3
};
// 單調(diào)乏味的重復(fù)"obj"
obj.a = 2;
obj.b = 3;
obj.c = 4;
// 簡單的快捷方式
with (obj) {
  a = 3;
  b = 4;
  c = 5;
}

但實際上這不僅僅是為了方便地訪問對象屬性??紤]如下代碼:

function foo(obj) {
  with (obj) {
  a = 2;
  }
}
var o1 = {
a: 3
};
var o2 = {
  b: 3
};
foo( o1 );
console.log( o1.a ); // 2
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2——不好,a 被泄漏到全局作用域上了!

eval(..)函數(shù)如果接受了含有一個或多個聲明的代碼,就會修改其所處的詞法作用域,而with聲明實際上是根據(jù)你傳遞給它的對象憑空創(chuàng)建了一個全新的詞法作用域。
另外一個不推薦使用eval(..)和with的原因是會被嚴(yán)格模式所影響(限制)。with被完全禁止,而在保留核心功能的前提下,間接或非安全地使用eval(..)也被禁止了。

人生不相見,動如參與商

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容