文中指出做用域有兩種工做模型,一種是動態做用域(不作討論),一種是詞法做用域,也是JS所採用的,說白了就是做用域是由咱們寫代碼的位置決定的。bash
好比:函數
function a(){
function b(){
// ...
}
}
複製代碼
那由b建立的做用域完徹底全被包含在a裏面,爲啥啊?性能
由於我們寫的時候就把函數b寫在了函數a裏面。這就是詞法做用域。ui
當引擎須要查找一個變量的時候,那麼會在本做用域中查詢,若是沒有的話便會逐層網上,直到全局做用域。做用域查找會在找到第一個匹配的標識符時中止,在多層的嵌套做用域中能夠定義同名的標識符,這叫作「遮蔽效應」。說白了就是,一旦找到了匹配的標識符,就算是外層還有一樣匹配的,也不找了。spa
let a = 'window.trio'
function foo(){
let a = 'foo.trio'
console.log(a) // foo.trio
}
foo()
複製代碼
可是!咱們能夠這樣子繞開遮蔽效應。code
var a = 'window.trio'
function foo(){
let a = 'foo.trio'
console.log(window.a) // window.trio
}
foo()
複製代碼
在JS中,由兩種機制,在運行的時候,能夠修改(欺騙)做用域。對象
eval(...) eval函數能夠接受一個字符串,並將其中的內容視爲好像在書寫時就存在於程序中的代碼,而且就在這個位置。作用域
function trio(str){
try{
console.log(a)
}catch(error){
console.log(error) // ReferenceError: a is not defined
}
eval(str)
console.log(a) // ② 不會報錯 打印123
}
trio('var a = 123')
複製代碼
咱們從try...catch...的結果能夠看出,eval()函數所傳入的參數並不會被編譯器所編譯(其實我知道根本就不可能編譯,那但是一個函數的參數啊,只有當函數執行的時候纔會有預編譯。可是我仍是忍不住想肯定一下。)。字符串
而在eval以後,再次打印a並不會報錯。由於引擎認爲②這行代碼自己就在那裏呢。編譯器
從編譯的結果來看,②這裏必報錯。可是真正執行的時候,卻沒有報錯,緣由就是eval修改了詞法做用域。
若是eval()一般傳入的是一個動態建立的代碼。
若是是eval函數能夠修改做用域,那麼with更像是建立了一個做用域。
with()函數能夠接受一個對象,而後根據對象去建立一個新的做用域
function foo(obj){
with(obj){
a = 2;
b = 3; // ①
var c = 4; // ②
}
}
var obj1 = {
a : 1
}
foo(obj1)
console.log(obj1.a) // 2
console.log(obj1.b) //undefined
console.log(window.b) //3
console.log(obj1.c,window.c) // undefined undefined
複製代碼
with能夠將一個沒有或有多個屬性的對象處理爲一個徹底隔離的詞法做用域,所以這個對象的屬性也會被處理爲定義在這個做用域中的詞法標識符。
!!!可是有個注意點!若是這個對象上沒有的屬性,單純的去賦值,如①,會泄露到全局做用域上,可是若是是var一個玩意兒並賦值,則不會泄露,也不會更改當前對象,如②。這兩點在後面的控制檯打印內容能夠體現。
使用這其中任何一個機制都會致使代碼運行的很慢,不要使用。雖然不用,但瞭解新的知識,老是好的,不是嗎?