當JavaScript代碼執行一段可執行代碼(executable code)時,會建立對應的執行上下文(execution context)。javascript
對於每一個執行上下文,都有三個重要屬性:html
前面已經講解了this,今天來說講做用域及做用域鏈。前端
做用域是指程序源代碼中定義變量的區域。java
做用域規定了如何查找變量,也就是肯定當前執行代碼對變量的訪問權限。git
JavaScript 採用詞法做用域(lexical scoping),也就是靜態做用域。github
所謂的詞法做用域,就是代碼在編寫過程就體現出來的做用範圍。代碼一旦寫好,不用執行, 做用範圍就已經肯定好了,這個就是所謂的詞法做用域。函數
詞法做用域的規則:post
*與詞法做用域相對的是動態做用域,函數的做用域是在函數調用的時候才決定的。ui
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
複製代碼
分析下執行過程:this
執行 foo 函數,先從 foo 函數內部查找是否有局部變量 value,若是沒有,就根據書寫的位置,查找上面一層的代碼,也就是 value 等於 1,因此結果會打印 1。
假設JavaScript採用動態做用域,讓咱們分析下執行過程:
執行 foo 函數,依然是從 foo 函數內部查找是否有局部變量 value。若是沒有,就從調用函數的做用域,也就是 bar 函數內部查找 value 變量,因此結果會打印 2。
看一個《JavaScript權威指南》中的例子:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
複製代碼
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
複製代碼
兩段代碼都會打印:local scope
。由於JavaScript採用的是詞法做用域,函數的做用域基於函數建立的位置。
引用《JavaScript權威指南》的回答就是:
JavaScript 函數的執行用到了做用域鏈,這個做用域鏈是在函數定義的時候建立的。嵌套的函數 f() 定義在這個做用域鏈裏,其中的變量 scope 必定是局部變量,無論什麼時候何地執行函數 f(),這種綁定在執行 f() 時依然有效。
當查找變量的時候,會先從當前上下文的變量對象中查找,若是沒有找到,就會從父級(詞法層面上的父級)執行上下文的變量對象中查找,一直找到全局上下文的變量對象,也就是全局對象。這樣由多個執行上下文的變量對象構成的鏈表就叫作做用域鏈。
這裏不具體介紹函數的[[scope]]屬性,只介紹一下怎麼簡單的分析做用域鏈。
做用域鏈繪製規則以下:
以代碼爲例:
function func1(){
alert(num);
}
function func2(){
var num=456;
function func3(){
func1();
}
func3();
}
func2();//結果顯示:num is not defined
複製代碼
上述代碼做用域鏈以下:
分析下代碼執行流程:
請注意:
相關係列: 從零開始的前端築基之旅(超級精細,持續更新~)
若是你收穫了新知識,就給做者點個贊吧~他急需支持~
參考文章:
- ****JavaScript深刻之詞法做用域和動態做用域****
- ****JavaScript深刻之做用域鏈****
- ****JS-詞法做用域 做用域鏈****