如何理解變數範圍

有C++基礎的人,對於變數最先感到困擾的是var沒有區塊(block)變數的概念,第二個是變數的可視範圍。

1.

function main() {
console.log(i);
for(var i=1; i<10; i++) {
...
}
console.log(i);
}

C++會找不到變數i,但Javascript的結果會是

undefined
10

關於這個問題,你可以想成JavaScript執行後,會將所有的東西塞進一個Object(執行環境,Execution context)裡面,而程式執行中所產生的變數,也會動態的加入這個Object中,這也就是為什麼沒有區塊變數的原因。

2.

var shareCount = 0;
app.get('/', function(req, res) {
var selfCount = 0;
shareCount++;
selfCount++;
setTimeout(function() {
res.json({total: shareCount, self: selfCount});
}, 1000);
})

前一篇提到的例子,有關變數範圍的部份,在這裡做個說明。shareCount定義在function外,屬於全域變數,而selfCount是在function內,屬於區域變數,Javascript在執行每一個函式時,都會建立一份新的執行環境,所以這就是selfCount會是獨立計算,而shareCount則會累計的原因。

有了前面提的觀念,closure就好懂得多。

function getTaxFunc(country) {
var rate = 1;
if(country == 'jp') rate = 1.08;
return function(consumption) {
return consumption*rate;
};
}
var jpTaxFunc = getTaxFunc('jp');
console.log('jp: ' + jpTaxFunc(100));
var twTaxFunc = getTaxFunc('tw');
console.log('tw: ' + twTaxFunc(100));

結果:

jp: 108
tw: 100

這個closure的觀念,你可以想成回傳的function只是一個reference,實際存在的地方仍在getTaxFunc範圍裡,如下圖拆解。

function getTaxFunc(country) {
var rate = 1;
if(country == 'jp') rate = 1.08;
var noNameFunc = function(consumption) {
return consumption*rate;
};
return noNameFunc;
}

因此,在其他地方呼叫jpTaxFunc(或twTaxFunc),都能存取得到rate,而且jpTaxFunc跟twTaxFunc所使用到的rate是不同的。

Execution context

上圖為使用WebStorm Debug時會印出的變數視窗,主程式會有jpTaxFunc跟twTaxFunc兩個區域變數,而執行到jpTaxFunc時,切換到獨立的執行環境,有自己的區域變數consumption,以及closure的區域變數rate。