靜态作用域:
一段代碼,在它執行之前就已經确定了它的作用域,
簡單來說就是在執行之前就确定了其可以應用哪些地方的作用域(意指:該作用域下的變量)。
以下例子為《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。
修改詞法作用域(不推薦)
在代碼書寫時,作用域(詞法作用域)就已經确定了,但是可不可以再修改呢?
通過eval和with都可以用來修改詞法作用域。
動态作用域:
函數的作用域是在函數調用的時候才決定的
在調用某個變量時,會從目前作用域逐級向上查找。
如果在目前作用域找到,就調用該變量,如果沒找到,就繼續向父級作用域查找,以此類推。
如果一直查找到最外層的全局作用域,都沒有找到該變量,那麼就表明沒有該變量。
注意差別于作用域鍊:函數在定義時,不光确定了它内部的作用域,還确定了它外部的作用域,也就是作用域鍊。
// 父級作用域變量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();
函數作用域
函數作用域的含義:屬于這個函數的全部變量可以在整個函數的範圍内使用及複用。
塊級作用域
{
let a = '塊級作用域'
}
console.log(a); //undefined
1.with
with也是塊作用域的一個例子。
2.try/catch
try/catch的catch分句也會建立一個塊作用域,其中聲明的變量僅在catch内部有效。
3.let
let關鍵字可以将變量綁定到所在的任意作用域中,換句話說,let為其聲明的變量隐式地劫持了所在的塊作用域。
用let将變量附加在一個已經存在的塊作用域的行為是隐式的。如果沒有密切關注哪些塊作用域中有綁定的變量,并且習慣性移動這些塊或者包含在其他塊中,就會導緻代碼混亂。
但是,用let進行的聲明不會在塊作用域中進行提升。聲明的代碼被運作之前,聲明并不存在。
4.垃圾收集
5.let循環
for循環頭部的let不僅将i綁定到for循環的塊中,事實上它将重新綁定到了循環的每一個疊代中,確定使用上一個循環疊代結束時的值重新進行指派。
for(let i=0;i<10;i++){
console.log(i);
}
console.log(i);
6.const
ES6引入了const,同樣可以用來建立塊作用域變量,但是其值是固定的。之後試圖修改值的操作都會引起錯誤。