JacaScript 訪問(wèn)塊級(jí)作用域

JavaScript 沒(méi)有塊級(jí)作用域的概念。這意味著在塊語(yǔ)句中定義的變量,實(shí)際上是在包含函數(shù)中而非語(yǔ)句中創(chuàng)建的。

function outputNumbers(count) {
    for(var i=0; i < count; i++) {
        alert(i);
    }

    alert(i); // 計(jì)數(shù)
}

上例函數(shù)中定義了一個(gè) for 循環(huán),而變量i的初始值被設(shè)置為0.在 Java、C++等語(yǔ)言中,變量i只會(huì)在 for 循環(huán)的語(yǔ)句塊中有定義,循環(huán)一旦結(jié)束,變量i就會(huì)被銷毀。可是在 JavaScript 中,變量i是定義在 outputNumbers() 的活動(dòng)對(duì)象中的,因此從它有定義開(kāi)始,就可以在函數(shù)內(nèi)部隨處訪問(wèn)它。即使重新聲明同一個(gè)變量,也不會(huì)改變它的值。

function outputNumbers(count) {
    for(var i=0; i < count; i++) {
        alert(i);
    }

    var i; // 重新聲明變量
    alert(i); // 計(jì)數(shù)
}

JavaScript 從來(lái)不會(huì)告訴你是否多次聲明了同一個(gè)變量;遇到這種情況,它只會(huì)對(duì)后續(xù)的聲明視而不見(jiàn)(不過(guò),它會(huì)執(zhí)行后續(xù)聲明中的變量初始化)。匿名函數(shù)可以使用模仿塊級(jí)作用域并避免這個(gè)問(wèn)題。

用作塊級(jí)作用域(通常稱為私有作用域)的匿名函數(shù)的語(yǔ)法:

(function() {
    // 這里是塊級(jí)作用域
})();

上例代碼定義并立即調(diào)用了一個(gè)匿名函數(shù)。將函數(shù)聲明包含在一對(duì)圓括號(hào)中,表示它實(shí)際上是一個(gè)函數(shù)表達(dá)式。而緊隨其后的另一對(duì)圓括號(hào)會(huì)立即調(diào)用這個(gè)函數(shù)。

var count = 5;
outputNumbers(count);

// 等價(jià)于
outputNumbers(5);

這里初始化變量 count,將其值設(shè)置為5.當(dāng)然這里的變量是沒(méi)有必要的,因?yàn)榭梢园阎抵苯觽鹘o函數(shù)。為了讓代碼更簡(jiǎn)潔,我們?cè)谡{(diào)用函數(shù)時(shí)用5來(lái)代替變量 count。

這樣做之所以可行,是因?yàn)樽兞恐徊贿^(guò)是值的另一種表現(xiàn)形式,因此用實(shí)際的值代替變量沒(méi)有問(wèn)題。

var someFunction = function() {
    // 這里是塊級(jí)作用域
}

someFunction();

// 等價(jià)于
(function() {
    // 這里是塊級(jí)作用域
})();

// 錯(cuò)誤
function() {
    // 這里是塊級(jí)作用域
}();

上例先定義了一個(gè)函數(shù),然后立即調(diào)用它。定義函數(shù)的方式是創(chuàng)建一個(gè)匿名函數(shù),并把匿名函數(shù)賦值給變量 someFunction。而調(diào)用函數(shù)的方式是在函數(shù)名稱后面添加一對(duì)圓括號(hào),即 someFunction()。

通過(guò)前面的例子知道,可以使用實(shí)際的值來(lái)取代變量 count ,那在這里是不是也可以用函數(shù)的值直接取代函數(shù)名呢?然而這么做會(huì)出錯(cuò)。

因?yàn)?JavaScript 將 function 關(guān)鍵字當(dāng)做一個(gè)函數(shù)聲明的開(kāi)始,而函數(shù)聲明后面不能跟圓括號(hào)。

然而,函數(shù)表達(dá)式的后面可以跟圓括號(hào)。要將函數(shù)聲明轉(zhuǎn)換成函數(shù)表達(dá)式,只要給他加上一對(duì)括號(hào)即可。

無(wú)論在什么地方,只要臨時(shí)需要一些變量,就可以使用私有作用域:

function outputNumbers(count) {
    (function() {
        for (var i=0; i < count; i++) {
            alert(i)
        }
    })()

    alert(i); // 發(fā)生錯(cuò)誤
}

在這個(gè)重寫(xiě)后的 outputNumbers() 函數(shù)中,我們?cè)?for 循環(huán)外部插入了一個(gè)私有作用域。在匿名函數(shù)定義的任何變量,都會(huì)在執(zhí)行結(jié)束時(shí)被銷毀。因此,變量i只能在循環(huán)中使用,使用后即被銷毀。而在私有作用域中能夠訪問(wèn)變量 count,是因?yàn)檫@個(gè)匿名函數(shù)是一個(gè)閉包,它能夠訪問(wèn)包含作用域中的所有變量。

這種技術(shù)經(jīng)常在全局作用域中被使用在函數(shù)外部,從而限制向全局作用域中添加過(guò)多的變量和函數(shù)。一般來(lái)說(shuō),我們都應(yīng)該盡量減少向全局作用域中添加變量和函數(shù)。在一個(gè)由很多開(kāi)發(fā)人員共同參與的大型應(yīng)用程序中,過(guò)多的全局變量和函數(shù)很容易導(dǎo)致致命沖突。而通過(guò)創(chuàng)建私有作用域,每個(gè)開(kāi)發(fā)人員既可以使用自己的變量,又不必?fù)?dān)心搞亂全局作用域:

(function() {
    var now = new Date();
    if (now.getMonth() == 0 && new.getDate() == 1) {
        alert("Happy new year!");
    }

})

把上面這段代碼放在全局作用域中,可以用來(lái)確定哪一天是 1月1日;如果到了這一天,就會(huì)向用戶顯示一條祝賀新年的消息,其中變量 now 現(xiàn)在是匿名函數(shù)中的局部變量,而我們不必在全局作用域中創(chuàng)建它。

這種做法可以減少閉包的內(nèi)存問(wèn)題,因?yàn)闆](méi)有指向匿名函數(shù)的引用。只要函數(shù)執(zhí)行完畢,就可以立即銷毀其作用域鏈了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容