執行上下文定義了變量或函數有權訪問得其餘數據,決定了它們各自得行爲。而在JavaScript中有三種執行上下文: 全局執行上下文,函數執行上下文,eval執行上下文。 代碼在其執行上下文中執行。在JavaScript中只有一個全局執行環境(根據宿主環境的不一樣,全局執行環境的對象也不同)。能夠有許多函數和eval執行環境的實例,每次調用一個函數或eval,都會進入對應執行環境代碼。注意 一個函數可能會產生無限上下文集合,由於每次函數調用自身都會產生一個新的執行上下文瀏覽器
執行上下文能夠激活另外一個執行上下文,例如函數調用另外一個函數(或者全局執行上下文調用全局函數)等等,邏輯上就成了一個堆棧。這被成爲執行上下文堆棧。markdown
當執行流進入一個函數時,函數的上下文就會被推入一個棧中,若是在當前函數中調用另外一個函數,當前函數就會被暫停執行,並將執行流傳遞給被調用函數(被調用函數同時多是其餘函數的調用者),被調用者函數推入堆棧。當被調用者的上下文結束後,將執行流交還給調用者,調用者的繼續運行代碼,直到結束,棧將上下文彈出。函數
做用域鏈本質上就是根據名稱查找變量(標識符名稱)的一套規則。規則很簡單,在本身的變量對象裏找不到變量,就上父級變量對象查找,當抵達最外層全局上下文中,不管找到仍是沒找到,查找過程都會中止。 查找會在找到第一個匹配的變量時中止,被稱爲遮蔽效應ui
var x = 10;
(function foo(){
var y = 10;
(function bar(){
var z = 10;
console.log(x+y+z)![scope-chain.png][6]
})
})
複製代碼
詞法做用域就是定義在詞法階段的做用域。換句話說,詞法做用域是由你在寫代碼時將變量和塊做用域寫在哪裏決定的, 不管函數在哪裏被調用,不管它如何被調用,它的詞法做用域只由函數被聲明位置決定。spa
在JavaScript中的eval函數能夠接受一個字符串爲參數,並將其中的內容視爲在書寫時就存在於程序中這個位置的代碼。code
function foo(str, a){
eval(str); //欺騙
console.log(a, b);
}
var b = 2;
foo("var b = 3;", 1); //1, 3
複製代碼
全局做用域在頁面打開時被建立,頁面關閉時被銷燬;在全局做用域中有全局對象window,表明一個瀏覽器窗口,由瀏覽器建立,能夠直接調用;全局做用域中聲明的變量和函數會做爲window對象的屬性和方法保存.orm
var a = 10;
b = 20;
function an(){
console.log('an')
}
var bn = function(){
console.log('bn')
}
console.log(window)
複製代碼
函數做用域有兩種方式對象
//函數聲明
function foo(){
var a = 3;
console.log(a);
}
複製代碼
//函數表達式
(function foo(){
var a = 2;
console.log(a);
})
複製代碼
二者的區別在於它們的名稱標識符會被綁定到何處,第一段代碼中會被綁定到所在的做用域中,第二段代碼被綁定在函數表達式自身的函數中而不是所在做用域中。ip
在JavaScript中沒有塊做用域,也就是說在{...}中聲明的變量會泄漏到外面做用域作用域
if(true){
var foo = 'dog'
}
console.log(foo); //dog
function dosomething(i){
console.log(i);
}
for(var i = 0; i < 10; i++){
dosomething(i);
}
console.log(i);
複製代碼
而在ES6中新增的let能夠將變量綁定到所在的任意做用域(一般是{...}內部),換句話說,let聲明的變量隱式的劫持了所在的塊做用域。
if(true){
var foo = 'dog'
}
console.log(foo); //dog
function dosomething(i){
console.log(i);
}
for(let i = 0; i < 10; i++){
dosomething(i);
}
console.log(i); //error
複製代碼
做用域實際上是有執行上下文中的變量對象和做用域鏈共同構成的。