javascript
沒有塊級作用域的概念。這意味着在塊語句中定義的變量,實際上是在函數中而非語句中建立的。從作用域鍊的角度來了解是,所有在函數内定義的變量(所有,也就是說塊語句中定義的變量也包含在内)都會在這個函數執行時所建立的函數的活動對象中,是以從函數内的所有變量定義開始,就可以在函數内部随處通路它。閉包也可以通過作用域鍊來通路它。
function outputNumbers(count){
for(var i = 0; i < count; i++){
console.log(i); // 0, 1, ... count - 1
}
console.log(i); // count
}
C++, JAVA等語言中,變量
i
隻會在
for
循環的語句塊(block)中有定義,循環一旦結束,變量
i
就會被銷毀。可是在
JavaScript
中,變量
i
是定義在
outputNumbers()
的活動對象中,是以從函數内的所有變量定義開始,就可以在函數内部随處通路它,閉包也可以通過作用域鍊通路它。即使像下面這樣重新聲明同一個變量,也不會改變它的值。
function outputNumbers(count){
for(var i = 0; i < count; i++){
console.log(i); // 0, 1, ... count - 1
}
var i; // redeclare i
console.log(i); // count
}
JavaScript從來不管是否多次聲明了同一個變量;遇到這種情況,JavaScript隻會對後續的聲明視而不見(不過會執行後續聲明中的變量初始化),将其當成一個指派語句。
函數包裝器可以用來模仿塊作用域并避免這個問題。
函數包裝器就是建立并立即調用一個函數。
(function(){
console.log("Hello World!");
})();
這段代碼直接輸出”Hello World”, 這就是一個函數包裝器。
函數包裝器的作用:
- 立即執行函數中的代碼,又不會再記憶體中留下對該函數的引用;
- 函數内部的所有變量都會被立即銷毀(除非将這些變量指派給了包含作用域中的變量)。
當在函數内部使用函數包裝器的時候,此時函數包裝器就是一個
閉包
,有權通路外部環境中的所有變量。
function outputNumbers(count){
(function(){
//塊級作用域
for(var i = 0; i < count; i++){
console.log(i); // 0, 1, ... count - 1
}
})();
console.log(i); // error
}
在函數包裝器中可以通路外部環境
outputNumbers()
的變量count,列印0, 1, … count - 1,但是在函數包裝器執行完畢之後,再通路變量i就會抛出錯誤,因為i是在函數包裝器中定義的,
outputNumbers()
函數無法通路。
無論在什麼地方,如果隻需要一些臨時變量,就可以使用塊級作用域!
使用函數包裝器這種閉包可以減少閉包過多占用記憶體的問題。因為沒有指向匿名函數的引用, 是以隻要函數包裝器執行完畢,就可以立即銷毀其作用域鍊了。
函數包裝器這種技術經常在全局作用域中被用在函數外部,進而限制想全局作用域中添加過多的變量和函數。一般來說,我們都應該盡量少向全局作用域中添加變量和函數。過多的全局變量和函數很容易導緻命名沖突。通過建立塊級作用域,每個開發人員既可以使用自己的變量,有不必擔心搞亂全局作用域。例如:
(function(){
var now = new Date();
if (now.getMonth() == 0 && now.getDate() == 1) {
console.log("Happy new year");
}
})();
将這段代碼放在全局作用域中,可以用來确定哪天是一月一日。其中變量now現在是匿名函數中的局部變量,避免了在全局變量中建立。