詳解javascript立即執行函式表示式(IIFE)

韓子遲發表於2015-06-15

寫在前面

這是一篇譯文,原文:Immediately-Invoked Function Expression (IIFE)

原文是一篇很經典的講解IIFE的文章,很適合收藏。本文雖然是譯文,但是直譯的很少,而且新增了不少自己的理解。

ps:下文中提到的“立即執行函式”其實就是“立即執行函式表示式”

我們要說的到底是什麼?

在javascript中,每一個函式在被呼叫的時候都會建立一個執行上下文,在該函式內部定義的變數和函式只能在該函式內部被使用,而正是因為這個上下文,使得我們在呼叫函式的時候能建立一些私有變數。

很多情況下我們並不需要像以上程式碼一樣初始化很多例項,甚至有時候並不需要返回值。

  • 問題的核心

現在我們定義了一個函式(function foo(){}或者var foo = function(){}),函式名後加上一對小括號即可完成對該函式的呼叫,比如下面的程式碼:

接著我們來看下面的程式碼:

報錯了,這是為何?這是因為在javascript程式碼解釋時,當遇到function關鍵字時,會預設把它當做是一個函式宣告,而不是函式表示式,如果沒有把它顯視地表達成函式表示式,就報錯了,因為函式宣告需要一個函式名,而上面的程式碼中函式沒有函式名。(以上程式碼,也正是在執行到第一個左括號(時報錯,因為(前理論上是應該有個函式名的。)

  • 一波未平一波又起

有意思的是,如果我們給它函式名,然後加上()立即呼叫,同樣也會報錯,而這次報錯原因卻不相同:

為什麼會這樣?在一個表示式後面加上括號,表示該表示式立即執行;而如果是在一個語句後面加上括號,該括號完全和之前的語句不搭嘎,而只是一個分組操作符,用來控制運算中的優先順序(小括號裡的先運算)。所以以上程式碼等價於:

相當於先宣告瞭一個叫foo的函式,之後進行()內的表示式運算,但是()(分組操作符)內的表示式不能為空,所以報錯。(以上程式碼,也就是執行到右括號時,發現表示式為空,所以報錯)。

如果想要了解更多,可以參考ECMA-262-3 in detail. Chapter 5. Functions.

立即執行函式(IIFE)

看到這裡,相信你一定迫不及待地想知道究竟如何做了吧,其實很簡單,只需要用括號全部括起來即可,比如下面這樣:

為什麼這樣就能立即執行並且不報錯呢?因為在javascript裡,括號內部不能包含語句,當解析器對程式碼進行解釋的時候,先碰到了(),然後碰到function關鍵字就會自動將()裡面的程式碼識別為函式表示式而不是函式宣告。

而立即執行函式並非只有上面的一種寫法,寫法真是五花八門:

  • 無論何時,給立即執行函式加上括號是個好習慣

通過以上的介紹,我們大概瞭解通過()可以使得一個函式表示式立即執行。

有的時候,我們實際上不需要使用()使之變成一個函式表示式,啥意思?比如下面這行程式碼,其實不加上()也不會保錯:

但是我們依然推薦加上():

為什麼?因為我們在閱讀程式碼的時候,如果function內部程式碼量龐大,我們不得不滾動到最後去檢視function(){}後是否帶有()來確定i值是個function還是function內部的返回值。所以為了程式碼的可讀性,請儘量加上()無論是否已經是表示式。

  • 立即執行函式與閉包的曖昧關係

立即執行函式能配合閉包儲存狀態。

像普通的函式傳參一樣,立即執行函式也能傳引數。如果在函式內部再定義一個函式,而裡面的那個函式能引用外部的變數和引數(閉包),利用這一點,我們能使用立即執行函式鎖住變數儲存狀態。

其實上面程式碼的lockedIndex也可以換成i,因為兩個i是在不同的作用域裡,所以不會互相干擾,但是寫成不同的名字更好解釋。以上便是立即執行函式+閉包的作用。

  • 我為什麼更願意稱它是“立即執行函式”而不是“自執行函式”

IIFE的稱謂在現在似乎已經得到了廣泛推廣(不知道是不是原文作者的功勞?),而原文寫於10年,似乎當時流行的稱呼是自執行函式(Self-executing anonymous function),接下去作者開始為了說明立即執行函式的稱呼好於自執行函式的稱呼開始據理力爭,有點咬文嚼字,不過也蠻有意思的,我們來看看作者說了些什麼。

我的理解是作者認為自執行函式是函式內部呼叫自己(遞迴呼叫),而立即執行函式就如字面意思,該函式立即執行即可。其實現在也不用去管它了,就叫IIFE好了。

  • 最後的旁白:模組模式

立即執行函式在模組化中也大有用處。用立即執行函式處理模組化可以減少全域性變數造成的空間汙染,構造更多的私有變數。

擴充套件閱讀

如果你願意瞭解更多內容,特別是關於函式和模組模式的內容,建議閱讀下列文章。

  1. ECMA-262-3 in detail. Chapter 5. Functions. – Dmitry A. Soshnikov
  2. Functions and function scope – Mozilla Developer Network
  3. Named function expressions – Juriy “kangax” Zaytsev
  4. JavaScript Module Pattern: In-Depth – Ben Cherry
  5. Closures explained with JavaScript – Nick Morgan

 

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

詳解javascript立即執行函式表示式(IIFE)

相關文章