JS全面系列教程詳解-06

內部函數

JS全面系列教程詳解-06

JavaScript 允許在一個函數內部定義函數,這一點我們在之前的 makePerson() 例子中也見過。關於 JavaScript 中的嵌套函數,一個很重要的細節是它們可以訪問父函數作用域中的變量:

JS全面系列教程詳解-06

如果某個函數依賴於其他的一兩個函數,而這一兩個函數對你其餘的代碼沒有用處,你可以將它們嵌套在會被調用的那個函數內部,這樣做可以減少全局作用域下的函數的數量,這有利於編寫易於維護的代碼。

這也是一個減少使用全局變量的好方法。當編寫複雜代碼時,程序員往往試圖使用全局變量,將值共享給多個函數,但這樣做會使代碼很難維護。內部函數可以共享父函數的變量,所以你可以使用這個特性把一些函數捆綁在一起,這樣可以有效地防止“汙染”你的全局命名空間——你可以稱它為“局部全局(local global)”。雖然這種方法應該謹慎使用,但它確實很有用,應該掌握。


閉包

下面我們將看到的是 JavaScript 中必須提到的功能最強大的抽象概念之一:閉包。但它可能也會帶來一些潛在的困惑。那它究竟是做什麼的呢?

JS全面系列教程詳解-06

makeAdder 這個名字本身應該能說明函數是用來做什麼的:它創建了一個新的 adder 函數,這個函數自身帶有一個參數,它被調用的時候這個參數會被加在外層函數傳進來的參數上。

這裡發生的事情和前面介紹過的內嵌函數十分相似:一個函數被定義在了另外一個函數的內部,內部函數可以訪問外部函數的變量。唯一的不同是,外部函數被返回了,那麼常識告訴我們局部變量“應該”不再存在。但是它們卻仍然存在——否則 adder 函數將不能工作。也就是說,這裡存在 makeAdder 的局部變量的兩個不同的“副本”——一個是 a 等於5,另一個是 a 等於20。那些函數的運行結果就如下所示:

JS全面系列教程詳解-06

下面來說說到底發生了什麼。每當 JavaScript 執行一個函數時,都會創建一個作用域對象(scope object),用來保存在這個函數中創建的局部變量。它和被傳入函數的變量一起被初始化。這與那些保存的所有全局變量和函數的全局對象(global object)類似,但仍有一些很重要的區別,第一,每次函數被執行的時候,就會創建一個新的,特定的作用域對象;第二,與全局對象(在瀏覽器裡面是當做 window 對象來訪問的)不同的是,你不能從 JavaScript 代碼中直接訪問作用域對象,也沒有可以遍歷當前的作用域對象裡面屬性的方法。

所以當調用 makeAdder時,解釋器創建了一個作用域對象,它帶有一個屬性:a,這個屬性被當作參數傳入 makeAdder 函數。然後 makeAdder 返回一個新創建的函數。通常 JavaScript 的垃圾回收器會在這時回收 makeAdder 創建的作用域對象,但是返回的函數卻保留一個指向那個作用域對象的引用。結果是這個作用域對象不會被垃圾回收器回收,直到指向 makeAdder 返回的那個函數對象的引用計數為零。

作用域對象組成了一個名為作用域鏈(scope chain)的鏈。它類似於原形(prototype)鏈一樣,被 JavaScript 的對象系統使用。

一個閉包就是一個函數和被創建的函數中的作用域對象的組合。

閉包允許你保存狀態——所以它們通常可以代替對象來使用。下一節會講關於閉包的詳細介紹。


以上是JavaScript中關於內部函數和閉包的的介紹以及應用場景,喜歡的話請多多關注支持下,每天都持續更新喲~


分享到:


相關文章: