作用域就是一個變數的生存範圍,一旦超出了這麼範圍就無法存取到這個變數。
在ES6之前,Javascript只擁有函數作用域和全域作用域,函式作用域也就是說每一個函數都有自己的作用域,在作用域外面無法存取到函數內定義的變數,全域作用域代表global任何地方都能存取到,而ES6引入的let與const,多了塊狀作用域的概念,
作用域式分層的,內層作用域可以取得外層作用域的變數,反之則不行。
全域作用域式全局性的,也就是說在整個Javascript程式中到處都可以使用,所有window物件
都屬於全域作用域中,全局作用域有個缺點,如果我們定義了很多變數、函示但卻沒有使用那麼它們就全部都在全局作用域中,會污染全局命名空間, 容易引起命名衝突。
var global = 123;
test = () => {
console.log(global); //123
};
test();
值得注意的是,尚未定義且直接賦值的變數都會自動宣告為全局作用域
這點在寫成式的時候會常常造成錯誤。
test = () => {
var func = 123;
global = 456;
console.log(func); //123
};
test();
console.log(func); //ReferenceError: func is not defined
console.log(global); //456 雖然global在函示內被賦值,但卻沒有定義型態,所以被自動歸類為全局作用域
函式作用域只有在函數內可以被訪問到,所以在函數內所宣告的變數不會洩漏到全域中造成全域汙染,而函式作用域可以訪問父層作用域的變數,但自身作用域的變數層級較高。
test = () => {
var a = 10;
};
console.log(a); //ReferenceError: a is not defined
var global = 123;
test = () => {
var global = 456;
console.log(global); //456
}
test();
在ES6中引入了let與const兩個變量宣告,新增了塊級作用域的概念。
由於ES5只有全局作用域與函數作用域,所以常常會造成一些想像不到的錯誤。
var tmp = new Date();
test = () => {
console.log(tmp);
if(false){
var tmp = "hello world";
}
};
test(); //undefined
在上面的程式中,雖然function可以讀取到全域的tmp變數,但是由於變量提升(hoisting)的關係導致輸出為undefined。
var tmp = new Date();
test = () => {
var tmp; //hoisting
console.log(tmp); //undefined
if(false){
tmp = "hello world";
}
};
test(); //undefined
而ES6新增了塊級作用域搭配上let,const不會hoisting的情況,可以避免常數的不確定性。
test = () => {
let n = 5;
if(true){
let n = 10;
}
//由於let n=5與console屬於同級塊狀作用域,而let n=10屬於下一層級的塊狀作用域,所以不會互相影響
console.log(n); //5
};
還有值得注意的地方,ES6的塊級作用域必須有大括號
,若沒有JavaScript則認為不存在塊級作用域。
//Error
if (true) let x = 1;
if (true) {
let x = 1;
}
參考資料 :
ECMAScript 6 入门
深入理解JavaScript作用域和作用域鏈
所有的函式都是閉包:談 JS 中的作用域與 Closure