在JavaScript中,你必須使用變量之前,通過var聲明變量:
> var x;
> x = 3;
> y = 4;
ReferenceError: y is not defined
你可以用一條var語句聲明和初始化多個變量:
var x = 1, y = 2, z = 3;
但我建議每個變量使用一條語句。因此,我將上面的語句重寫為:
var x = 1;
var y = 2;
var z = 3;
由于提升(見下文),最好在函數(shù)頂部聲明變量。
變量的作用域總是整個函數(shù)(沒有塊級作用域)。例如:
function foo() {
var x = -3;
if (x < 0) { // (*)
var tmp = -x;
...
}
console.log(tmp); // 3
}
我們可以看到tmp變量不僅在(*
)所在行的語句塊存在,它在整個函數(shù)內(nèi)都存在。
變量聲明會被提升:聲明會被移到函數(shù)的頂部,但賦值過程不會。舉個例子,在下面的函數(shù)中(*
)行位置聲明了一個變量。
function foo() {
console.log(tmp); // undefined
if (false) {
var tmp = 3; // (*)
}
}
在內(nèi)部,上面的函數(shù)被執(zhí)行像下面這樣:
function foo() {
var tmp; // declaration is hoisted
console.log(tmp);
if (false) {
tmp = 3; // assignment stays put
}
}
每個函數(shù)保持和函數(shù)體內(nèi)部變量的連接,甚至離開創(chuàng)建它的作用域之后。例如:
function createIncrementor(start) {
return function () { // (*)
return start++;
}
}
在(*
)行開始的函數(shù)在它創(chuàng)建時保留上下文,并在內(nèi)部保存一個start活動值:
> var inc = createIncrementor(5);
> inc()
5
> inc()
6
> inc()
7
閉包是一個函數(shù)加上和其作用域鏈的鏈接。因此,createIncrementor() 返回的是一個閉包。
有時你想模擬一個塊,例如你想將變量從全局作用域隔離。完成這個工作的模式叫做 IIFE(立即執(zhí)行函數(shù)表達式(Immediately Invoked Function Expression)):
(function () { // 塊開始
var tmp = ...; // 非全局變量
}()); // 塊結束
上面你會看到函數(shù)表達式被立即執(zhí)行。外面的括號用來阻止它被解析成函數(shù)聲明;只有函數(shù)表達式能被立即調用。函數(shù)體產(chǎn)生一個新的作用域并使 tmp 變?yōu)榫植孔兞俊?/p>
下面是個經(jīng)典問題,如果你不知道,會讓你費盡思量。因此,先瀏覽下,對問題有個大概的了解。 閉包保持和外部變量的連接,有時可能和你想像的行為不一致:
var result = [];
for (var i=0; i < 5; i++) {
result.push(function () { return i }); // (*)
}
console.log(result[1]()); // 5 (不是 1)
console.log(result[3]()); // 5 (不是 3)
(*
)行的返回值總是當前的i值,而不是當函數(shù)被創(chuàng)建時的i值。當循環(huán)結束后,i的值是5,這是為什么數(shù)組中的所有函數(shù)的返回值總是一樣的。如果你想捕獲當前變量的快照,你可以使用 IIFE:
for (var i=0; i < 5; i++) {
(function (i2) {
result.push(function () { return i2 });
}(i)); // 復制當前的i
}
更多建議: