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