JavaScript踩坑筆記07---作用域鏈、動態作用域、靜態作用域、詞法作用域
JavaScript踩坑筆記07---作用域鏈、動態作用域、靜態作用域、詞法作用域
作用域鏈:
函式在定義時,不光確定了它內部的作用域,還確定了它外部的作用域,也就是作用域鏈。
舉例說明。
// 全域性變數num,boo
var num = 1;
var boo;
function fn1() {
function fn2() {
function fn3() {
// 依次從父級作用域找同名自由變數
console.log(num); // 1
// 直到全域性作用域下也沒找到該變數,證明該變數未宣告,程式報錯
console.log(str); // ReferenceError: str is not defined
// 在全域性作用域下找到變數boo,雖然已宣告,但是並沒有初始化,所以未定義
console.log(boo); // undefined
}
fn3();
}
fn2();
}
fn1();
在上述例子中,我定義了三個函式,並且依次巢狀。在函式fn3中並沒有變數num,那麼就只能找來自父作用域的同名自由變數,父作用域也沒找到,再向上一級父作用域找同名的自由變數,最終找到變數num,如果最終沒有找到該變數,那就證明該變數未宣告,或未定義。這就是作用域鏈。
所以說找一個變數,只需要向其父作用域逐級向上找,就可以了嗎?
JavaScript的作用域可沒這麼簡單,要分清函式被呼叫時的作用域和函式定義時的作用域。下面動態作用域和靜態作用域會說明。
動態作用域:
動態作用域指的是,在呼叫某個變數時,會從當前作用域逐級向上查詢。
如果在當前作用域找到,就呼叫該變數,如果沒找到,就繼續向父級作用域查詢,以此類推。
如果一直查詢到最外層的全域性作用域,都沒有找到該變數,那麼就表明沒有該變數。
舉例說明。
// 父級作用域變數num1
var num1 = 1;
function fn() {
// 當前作用域變數num2
var num2 = 2;
// 在當前作用域沒有找到變數num1,向父級作用域查詢,在父級作用域找到變數num1,直接呼叫
console.log(num1); // 1
// 在當前作用域找到變數num2,直接呼叫
console.log(num2); // 2
// 在當前作用域沒有找到變數num3,向父級作用域查詢,在父級全域性作用域也沒找到變數num3,證明該變數未定義
console.log(num3); // ReferenceError: num3 is not defined
}
fn();
靜態作用域、詞法作用域:
靜態作用域,又叫作詞法作用域,函式在定義的時候,就已經確定了函式體內部自由變數的作用域。
JavaScript採用的是靜態作用域。
舉例說明。
// 全域性變數num
var num = "1";
function show() {
function fn1() {
console.log(num);
}
function fn2() {
// 區域性變數num
var num = 2;
fn1();
}
fn2();
}
show(); // 1
當執行函式fn時,會首先在函式show內部查詢變數num,如果找到,則直接呼叫,如果沒找到,就到函式show定義的作用域下查詢,如果找到,則直接呼叫,如果沒找到,則會逐級向上查詢。
動態作用域與靜態作用域的區別:
動態作用域的查詢規則是程式執行時的函式呼叫順序。
靜態作用域的查詢規則是距離當前作用域(宣告的位置)最近的外層作用域中的同名變數。
《JavaScript權威指南》經典例子:
以下例子為《JavaScript權威指南》中經典的一個例子,基本網上的每個大佬都會提及。
例一:
var scope = "global scope";
function checkScope() {
var scope = "local scope";
function fn() {
return scope;
}
return fn();
}
checkScope(); // local scope
分析:
在函式checkScope中定義了函式fn,並將函式fn的返回值當作函式checkScope的返回值返回。
首先在函式fn的內部作用域中找變數scope,沒找到變數,所以在函式fn當前所在的作用域下找變數scope,找到變數,直接返回,所以返回的結果是local scope。
例二:
var scope = "global scope";
function checkScope() {
var scope = "local scope";
function fn() {
return scope;
}
return fn;
}
checkScope()(); // local scope
分析:
在函式checkScope中呼叫函式fn,函式fn同作用域下定義了變數scope,所以輸出的是local scope。
在函式checkScope中定義了函式fn,並將函式fn當作函式checkScope的返回值返回,此時函式checkScope返回的是一個函式物件,不管這個返回的函式在哪裡執行,當他定義時,他的作用域就已經確定了。
所以首先在函式fn的內部作用域中找變數scope,沒找到變數,所以在函式fn當前所在的作用域下找變數scope,找到變數,直接返回,所以返回的結果還是local scope。
個人學習總結,歡迎批評指正