類 C 語言一般都用於塊級範圍,但是Javascript中的 while, if-else, for, switch-case 等控制結構不具有自己的範圍,
在Javascript中只有函數(function)具有自己的範圍,請比較如下的 C# 代碼和 Javascript 代碼:
// C# 中 for 迴圈有自己的範圍
for (int i = 0; i < 5; i++)
{
//
}
Console.WriteLine(i); // 當前上下文中不存在名稱“i”
// Javascript中 for 迴圈沒有自己的範圍
for (var i = 0; i < 5; i++) {
// do something
}
console.log(i); // 5
但是,Javascript中的函數有自己的範圍,如下樣本:
// function 擁有自己的範圍
(function() {
var j = 6;
})();
console.log(typeof (j)); // 'undefined'
// function 中使用和全域變數相同名稱的局部變數
var i = 5;
(function() {
var i = 1;
})();
console.log(i); // 5
在函數中不使用 var 關鍵字聲明的變數,就會在全域範圍內尋找,如下:
var i = 5;
(function() {
i++; // 此時的i是全域變數
})();
console.log(i); // 6
這裡就有一個可能讓人迷惑的代碼,如下:
var i = 6;
(function() {
i++; // 此時的i是局部變數,但是未定義(undefined),undefined + 1 == NaN
console.log(isNaN(i)); // true
var i;
})();
console.log(i); // 6
或許你會覺得函數中 i++, 引用的豈不是全部變數?
答案是否定的,只要在函數內擁有相同名稱的局部變數定義,那這個變數就是局部的。
那我們如何在函數內引用和局部變數相同名稱的全域變數呢?
答案是使用命名首碼。考慮如下代碼:
var i = 6;
(function() {
window.i++; // 此時的i是局部變數
var i = 1;
})();
console.log(i); // 7
正因為自執行的匿名函數,可以用來向外部隱藏屬性,我們可以用這個特性在Javascript中實現私人屬性。
考慮如下擷取隨機數([0,10])的對象:
var random1 = {
_num: -1,
_init: function() {
// 隨機一個大於等於0,小於等於10的整數
this._num = Math.round(Math.random() * 10);
},
getNum: function() {
if (this._num === -1) {
this._init();
}
return this._num;
}
};
console.log('random1._num == ' + random1._num); // -1
console.log('random1.getNum() == ' + random1.getNum()); // [0,10]
雖然我們的意圖是想向外界隱藏 _num 變數和 _init 屬性,不過這個目的顯然沒有達到,使用者仍然可以通過 random1._init(); 的方式調用。
我們需要是用Javascript中函數特有的性質(擁有自己的範圍),使用自執行的匿名函數來達到隱藏屬性的目的。
考慮如下解決辦法:
var random2 = {};
(function() {
var _num = -1;
function _init() {
_num = Math.round(Math.random() * 10);
}
random2.getNum = function() {
if (_num === -1) {
_init();
}
return _num;
};
})();
console.log('random2._num == ' + random2._num); // 'undefined'
console.log('random2.getNum() == ' + random2.getNum()); // [0,10]
代碼下載