# IIFEs 立即函式 >**Immediately Invoked Function Expressions 即刻調用的函式運算式** >指的就是透過 function expression(函式運算式) 的方式來建立函式,並且立即執行它 <br> #### Question Explain why the following doesn't work as an IIFE: `function foo(){ }();`. What needs to be changed to properly make it an IIFE? <br> ## 前言 ### 表達式(Expressions)和 陳述句(Statements)的差異 #### 表達式(Expressions) Expressions 指的是輸入後能夠直接回傳值的一串程式(**a unit of code that results in a value**) ![](https://i.imgur.com/tJ8u0Xy.png =400x) <br> #### 陳述句(Statements) ```+ if (a === 3) { console.log('Hello'); } ``` 在這段程式中 `a === 3` 是一個表達式(expression),因為它可以直接回傳值(即,`true` 或 `false`);而 if 這個指令,則是一個 statement <br> **** ## Function Statement(Declaration) 函式述句(宣告) v.s. Function Expression 函式運算式 > **Functions are objects** ### Function Statements ```+ function greet() { console.log('Hi'); } ``` ![](https://i.imgur.com/2SkiYzE.png =600x) <br> ### Function Expressions 因為在 JavaScript 中 Function 就是物件的一種,所以我們可以把它存在一個變數中: ```+ const sayHello = function() { console.log('Hello'); }; ``` 這裡的 `function(){ ... }` 這段就是 Function Expression,現在我們則把這個函式表達式的值存在 `sayHello` 這個變數內。 ![](https://i.imgur.com/ABpg2Ft.png =600x) <br> ***和 Function Statements 不同的地方 ( 補充 )*** 使用 Function expression 的話不能在定義前呼叫它 ```+ sayHello(); const sayHello = function() { console.log('Hello'); }; ``` ![](https://i.imgur.com/7Vi6IMa.png =450x) <br> **** ## IIFE 立即函式 #### Function Statement 當 syntax parser 在一行程式碼的最前面看到 `function` 時,就會認定他是一個 function statement ```+ function foo(){ console.log("foo"); } ``` >**所以當我們寫出`function foo(){}()` 時,syntax parser 認為在一個 function statement 後看到不應該出現的`()` 因此顯示錯誤 ** ![](https://i.imgur.com/nPMAbU8.png =450x) <br/> #### 轉為 Function Expression 當我們用 `()` 把一個 function statement 包裹起來,函式不會被當成一個標準的 function statement (declaration),而是**當作一個函式運算式 function expression** *註:`()` 在這裡會被當成一個 expression 中 grouping 的 operator 運算子,像是 `(2+3)*4` 一樣 * ```+ (function foo(){ console.log("foo"); }) ``` ![](https://i.imgur.com/MlHf22U.png =250x) <br/> #### Immediately Invoked Function Expressions 在我們建立函式的同時,加上一個執行的指令,也就是括號 `()` 這段函式就會立即被執行了 ```+ (function foo(){ console.log("foo"); })() ``` ![](https://i.imgur.com/85Vsq1j.png =250x) <br /> #### IIFEs 的另一種寫法 兩種都可以,是個人選擇,不過 prettier 等等會校正成另一種 ```+ (function (name) { console.log("Hello " + name); }()) ``` <br> ##### 另一種例子 (較少見) ```+ const greetMyself = function(name) { return('Welcome ' + name); }("WYT"); console.log(greetingMySelf); //Welcome WYT ``` <br> *** #### Anonymous IIFE 匿名立即函式 ###### 錯誤範例 syntax parser 首先看到 `function` keyword,認定他是一個 function statement,syntax parser 找不到這個 function 的名字,於是它無法正確理解這段程式碼,而拋出錯誤: ```+ function (name) { console.log("Hello " + name); } ``` ![](https://i.imgur.com/GGv9zei.png =350x) <br /> ##### 用 `()` 包裹匿名函式讓它變成 function expression syntax parser 首先看到 `()` keyword,認定他是一個 expression,因為 ```+ (function (name) { console.log("Hello " + name); }) ``` ![](https://i.imgur.com/8R9euXj.png =300x) <br /> ##### 用另一對`()` 傳入argument,執行 function ```+ (function (name) { console.log("Hello " + name); })("I am anonymous IIFE") ``` ![](https://i.imgur.com/wC8A5lH.png =300x) <br> **** ### 用 IIFE 讓全域環境不受污染 > 不少JS框架、套件的開頭與結尾被`()`包住,程式碼被立即函式包著,其目的是怕污染到使用者(開發者)的全域環境。 在ES6出來前,為了避免設定太多的全域變數,開發者往往會將變數設定在函式中,使其成為區域變數,尤其是設定在IIFE中,確保不會汙染到全域環境的變數。 ```+ const seafood = '波士頓龍蝦'; (function(name){ const seafood = '北海道帝王蟹'; console.log(name + '好想吃' + seafood); }('Simon')); // Simon好想吃北海道帝王蟹 ``` 因為執行環境的不同,'北海道帝王蟹'只存在IIFE裡,不會影響到外部環境的變數。 也就是說,我們可以將程式碼包在被()包住的IIFE裡,當IIFE和全域環境有宣告相同變數名稱時,IIFE不會蓋掉全域的變數。 **** ### 總結 IIFE 用途 使用立即函式的好處,除了可以立即執行程式碼,省略多餘的呼叫,還可以用來避免汙染全域執行環境的東西,減少開發時因相同命名相衝的bug。 **** ### 參考資料 - Udemy - JavaScript: Understanding the Weird Part (lecture 34, 35, 44 ) - 你你所不知道的JS|範疇與Closures,this 與物件原型 p.34~p.38 - [[筆記] 進一步談JavaScript中函式的建立─function statements and function expressions](https://pjchender.blogspot.com/2016/03/javascriptfunction-statements-and.html) - [[筆記] 談談JavaScript中的IIFEs(Immediately Invoked Functions Expressions)](https://pjchender.blogspot.com/2016/05/javascriptiifesimmediately-invoked.html) - [Day20 立即呼叫的函式表示式(IIFE)](https://ithelp.ithome.com.tw/articles/10193313)