在JavaScript中,您在使用變量之前先聲明它們:var
> var x;
> x
未定義
> y
ReferenceError:y未定義
您可以使用一個var 語句來聲明和初始化幾個變量:
var x = 1, y = 2, z = 3;
但是我建議每個變量使用一個語句(原因在Syntax中進行了解釋)。因此,我將前面的語句重寫為:
var x = 1;
var y = 2;
var z = 3;
由于提升(請參見變量提升),通常最好在函數的開頭聲明變量。
變量是函數范圍的
變量的范圍始終是完整的功能(與當前塊相反)。例如:
function foo() {
? ? var x = -512;
? ? if (x < 0) {? // (1)
? ? ? ? var tmp = -x;
? ? ? ? ...
? ? }
? ? console.log(tmp);? // 512
}
我們可以看到變量tmp不限于從第(1)行開始的塊。它存在直到函數結束。
變量被吊起
每個變量聲明都是懸掛的:該聲明將移至該函數的開頭,但保留的賦值將保留。例如,請考慮以下函數中第(1)行中的變量聲明:
function foo() {
? ? console.log(tmp); // undefined
? ? if (false) {
? ? ? ? var tmp = 3;? // (1)
? ? }
}
在內部,前面的函數是這樣執行的:
function foo() {
? ? var tmp;? // hoisted declaration
? ? console.log(tmp);
? ? if (false) {
? ? ? ? tmp = 3;? // assignment stays put
? ? }
}
關閉
每個功能 即使它離開了創建它的作用域,它仍然與圍繞它的函數的變量保持聯系。例如:
function createIncrementor(start) {
? ? return function () {? // (1)
? ? ? ? start++;
? ? ? ? return start;
? ? }
}
從第(1)行開始的函數會離開其創建時所在的上下文,但仍保持與以下版本的實時連接start:
> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8
一個封閉的功能加上其周邊范圍的變量的連接。因此,createIncrementor()返回的是封閉。
IIFE模式:引入新的范圍
有時您想引入一個新的 變量作用域-例如,防止變量成為全局變量。在JavaScript中,您不能使用塊來這樣做。您必須使用一個函數。但是,存在一種以塊狀方式使用功能的模式。它稱為IIFE(立即調用的函數表達式,發音為“ iffy”):
(function () {? // open IIFE
? ? var tmp = ...;? // not a global variable
}());? // close IIFE
確保完全按照顯示的方式鍵入前面的示例(注釋除外)。IIFE是定義后立即調用的函數表達式。在函數內部,存在新的作用域,以防止其tmp成為全局變量。有關IIFE的詳細信息,請咨詢通過IIFE引入新范圍。
IIFE用例:通過關閉無意間共享
閉包保持它們與外部變量的連接,有時這不是您想要的:
var result = [];
for (var i=0; i < 5; i++) {
? ? result.push(function () { return i });? // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)
第(1)行中返回的值始終是的當前值i,而不是創建函數時的值。循環完成后,i值為5,這就是數組中所有函數都返回該值的原因。如果希望第(1)行中的函數接收的當前值的快照,則i可以使用IIFE:
for (var i=0; i < 5; i++) {
? ? (function () {
? ? ? ? var i2 = i; // copy current i
? ? ? ? result.push(function () { return i2 });
? ? }());
}