天天看點

《深入了解JavaScript》——1.13 變量作用域和閉包

本節書摘來自異步社群《深入了解javascript》一書中的第1章,第1.13節,作者: 【美】axelrauschmayer(羅徹麥爾)譯者: 王玉林 , 杜歡 , 莊婷婷 , 章子鵬,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

在javascript中,通過在變量前使用var語句聲明變量:

  

《深入了解JavaScript》——1.13 變量作用域和閉包

你可以使用單個var語句聲明和初始化多個變量:

《深入了解JavaScript》——1.13 變量作用域和閉包

但是我推薦使用單獨聲明每一個變量(原因參考26.4.1“文法”)。是以,我會将之前的語句重寫為:

《深入了解JavaScript》——1.13 變量作用域和閉包

由于前置的緣故(參考1.13.2“變量的提升特性”),通常它的最佳實踐是在一個函數的開始部分聲明變量。

1.13.1 變量是函數作用域的

一個變量的作用域總是完整的函數(相對于目前塊)。例如:

《深入了解JavaScript》——1.13 變量作用域和閉包

我們可以看到變量tmp并不局限于(1)行;直到函數結束它都存在。

1.13.2 變量的提升特性

所有變量聲明都會被提升:聲明會被移動到函數的開始處,而指派則仍然會在原來的位置進行。例如,以下函數中的變量會被認為是在标記為(1)的這行聲明的:

《深入了解JavaScript》——1.13 變量作用域和閉包

然而在程式内部,上述函數的執行過程其實是這樣的:

《深入了解JavaScript》——1.13 變量作用域和閉包

1.13.3 閉包

每個函數都和它周圍的變量保持着連接配接,哪怕它離開被建立時的作用域也是如此。例如:

《深入了解JavaScript》——1.13 變量作用域和閉包

函數從标記為(1)的這行開始被建立,在建立結束後即離開它的上下文環境,但它仍然保持着和start的連接配接:

《深入了解JavaScript》——1.13 變量作用域和閉包

函數以及它所連接配接的周圍作用域中的變量即為閉包。是以,create incrementor()的傳回其實就是一個閉包。

1.13.4 iife模式:引入一個新的作用域

有時你會想要引入一個新的作用域,例如,防止一個變量變成全局變量。在javascript中,不能通過塊來做,必須使用函數。不過有一種模式可以将函數當做類似塊的方式來使用。這種模式被稱作為iife(立即調用函數表達式,發音為“iffy”):

《深入了解JavaScript》——1.13 變量作用域和閉包

請務必鍵入以上示例(注釋除外)。iife是一個在定義之後就被立即調用的函數表達式。在函數内部,會有一個新的作用域,以防止tmp變成全局變量。更多關于iife的細節,參見16.6“通過iife引入新的作用域”。

iife用例:閉包造成的無意共享

閉包會持續地保持與外部變量的連接配接,而這有時候并不是你想要的:

 

《深入了解JavaScript》——1.13 變量作用域和閉包

标記為(1)的這行傳回值總是i的目前值,而并非函數被建立時的值。在循環結束之後,i的值為5,是以數組中所有的函數都傳回這個數值。如果你想要标記(1)這行的函數獲得目前i值的一個快照,那麼你可以使用iife:

《深入了解JavaScript》——1.13 變量作用域和閉包

繼續閱讀