JS做用域

執行上下文(也稱執行環境)堆棧

執行上下文定義了變量或函數有權訪問得其餘數據,決定了它們各自得行爲。而在JavaScript中有三種執行上下文: 全局執行上下文,函數執行上下文,eval執行上下文。 代碼在其執行上下文中執行。在JavaScript中只有一個全局執行環境(根據宿主環境的不一樣,全局執行環境的對象也不同)。能夠有許多函數和eval執行環境的實例,每次調用一個函數或eval,都會進入對應執行環境代碼。注意 一個函數可能會產生無限上下文集合,由於每次函數調用自身都會產生一個新的執行上下文瀏覽器

89241819-570d14fe33067.png

執行上下文能夠激活另外一個執行上下文,例如函數調用另外一個函數(或者全局執行上下文調用全局函數)等等,邏輯上就成了一個堆棧。這被成爲執行上下文堆棧。markdown

當執行流進入一個函數時,函數的上下文就會被推入一個棧中,若是在當前函數中調用另外一個函數,當前函數就會被暫停執行,並將執行流傳遞給被調用函數(被調用函數同時多是其餘函數的調用者),被調用者函數推入堆棧。當被調用者的上下文結束後,將執行流交還給調用者,調用者的繼續運行代碼,直到結束,棧將上下文彈出。函數

620422871-570d14feaf681_fix732.png

做用域鏈(scope chain)

做用域鏈本質上就是根據名稱查找變量(標識符名稱)的一套規則。規則很簡單,在本身的變量對象裏找不到變量,就上父級變量對象查找,當抵達最外層全局上下文中,不管找到仍是沒找到,查找過程都會中止。 查找會在找到第一個匹配的變量時中止,被稱爲遮蔽效應ui

var x = 10;
(function foo(){
    var y = 10;
    (function bar(){
        var z = 10;
        console.log(x+y+z)![scope-chain.png][6]
    })
})
複製代碼

1712643983-570d14ffae847.png

詞法做用域

詞法做用域就是定義在詞法階段的做用域。換句話說,詞法做用域是由你在寫代碼時將變量和塊做用域寫在哪裏決定的, 不管函數在哪裏被調用,不管它如何被調用,它的詞法做用域只由函數被聲明位置決定。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

複製代碼

總結

做用域實際上是有執行上下文中的變量對象和做用域鏈共同構成的。

相關文章
相關標籤/搜索