JS 的每個函式都擁有自己的作用域,可以說,當你宣告了一個函式,同時就創建了一個新的作用域。
函式作用域的規則如下:
function foo(a) {
var b = 2;
function bar() {
// ...
}
console.log( a, b, c ); // 1 2 undefined
var c = 3;
bar(); // 成功
}
foo(1);
bar(); // ReferenceError
console.log( a, b, c ); // 3个都失败,回報 ReferenceError
說完函式作用域的基礎規則後,下面來看看不同種類的函式,以及它們的作用域如何運作。
匿名函式範例:
setTimeout( function(){
console.log("I waited 1 second!");
}, 1000 );
function
作為語句開頭的函式function
作為語句開頭的函式var a = 2;
(function foo(){ // <-- 插入 "("
var a = 3;
console.log( a ); // 3
})(); // <-- 插入 ")()"
foo(); // ReferenceError
console.log( a ); // 2
如以上範例,函式 foo
在定義時並非以 function
作為語句開頭,因此是一段函式表達式,函式識別字 foo
以及內部變數都屬於函式內部,無法從外部存取。
函式宣告的規則可以直接參考文章最開頭的敘述通則,以下來討論函式表達式要注意的部分。
將一個匿名函式賦值給一個變數,與函式宣告的不同之處和提升(Hoisting)有關。
函式宣告的內容會被提升,但函式表達式並沒有提升,之後關於提升的文章將詳細討論這一點。
var func = function() {
//Your Code Here
};
在原地馬上進行調用的函式,可具名也可匿名,但即便是具名函式也無法被外部作用域取得,調用完畢後會立刻被回收。
var a = 2;
(function foo(){
var a = 3;
console.log( a ); // 3
console.log( foo ); // ƒ foo(){ ... }
})();
console.log( a ); // 2
console.log( foo ); // ReferenceError: foo is not defined
IIFE 的另一種寫法:
(function(){ .. }())
,兩者的差異只有調用的括號所在位置。
由於 IIFE 擁有只屬於自己的作用域,以及用後立即回收的特性,它也是經常被使用的一種函式。
指定範疇:將傳入的參數當作作用域
var a = 2;
(function IIFE(global) {
var a = 3;
console.log(a); // 3
console.log(global.a); // 2
})(window);
console.log(a); // 2
確保 undefined 的正確性
undefined = true;
(function IIFE(undefined) {
var a;
if (a === undefined) {
console.log('Undefined 在這裡很安全!');
}
})();
反轉順序
如通用模組定義(UMD, Universal Module Definition)
var a = 2;
(function IIFE(def) {
def(window);
})(function def(global) {
var a = 3;
console.log(a); // 3
console.log(global.a); // 2
});