L4: 立即呼叫的函數表示式(IIFEs) == ###### tags: `JavaScript` Immediately Invoked Function Expressions 函數創造後就立刻執行 從範例來了解什麼是IIFE 一般的函數寫法大多是下面這樣 ```javascript! // function statement function greet(name) { console.log( 'Hello ' + name) } console.log(greet('John')) // Hello John // using a function expressions JS引擎會立刻創造函數物件 const greetFunc = function (name) { return( 'Hello ' + name) } console.log(greetFunc('John')) // Hello John ``` 如果在Function Expressions 後面加上(),讓函數被創造之後立刻呼叫他,就是**立即呼叫函數**,建立函數表示式後立刻呼叫他,所以如果沒有加上(),`console.log(greeting)`會得到函數本身 也可以直接在()中加入參數,注意`console.log(greeting)` 已經是一個字串不是函數,因為 greet 等於 function greet 執行完回傳的值-> `return( 'Hello ' + name)` -> `Hello John` ```javascript! const greeting = function (name) { return( 'Hello ' + name) } console.log(greeting) // function (name) {return( 'Hello ' + name) } console.log(greeting()) // Hello undefined // 加上括號 using an Immediately Invoked Function Expression const greeting = function (name) { return( 'Hello ' + name) }() console.log(greeting) // Hello undefined // 直接在括號中加入參數 const greeting = function (name) { return( 'Hello ' + name) }('John') console.log(greeting) // // Hello John ``` ==另一種IIFE寫法,用括號把Function Expressions包起來==,也是最常見的寫法 ```javascript! // error syntax function (name) { return 'Hello ' + name } ``` 如果直接在全域環境寫匿名函數會出錯(`Uncaught SyntaxError: Function statements require a function name`),因為語法解析器看到function在一行程式碼的最前面,或是接在分號之後,所以它預期這是一個函數陳述句,它要要有個名稱,不可以是匿名,**我們要怎麼讓語法解析器知道不要把它變成函數陳述句呢?** ==我們要確保function這個字不是一行程式碼中的第一個字== ```javascript! (function (name) { return 'Hello ' + name }) ``` 改成==用括號包住匿名函數==,()括號在JS裡是一個運算子,JS知道在括號裡的東西一定是表示式,我們在同一個時間創造函數並執行它 加上參數,在()後面再加上('John'),立刻執行,會得到`Hello John`的結果 ```javascript! (function (name) { const greeting = 'Hello' console.log( greeting + ' ' + name) // Hello John }('John')) ``` 這是標準的IIFE,立即呼叫的函數表示式,這對JavaScript開發者是一個很棒的工具,在所有大型framework和librart裡面看到這樣的東西 另外,也可以把呼叫的括號寫在外面,都是可以運行的,選一個自己喜歡的方式 ```javascript! (function (name) { const greeting = 'Hello' console.log( greeting + ' ' + name) // Hello John })('John') ``` ### <font color="#3733FF">IIFES and safe code</font> ==使用IIFE的的好處是,把程式碼包在裡面後,保證他不會和全域裡的東西產生衝突,也不會被污染== 1. 程式被載入時,先建立全域執行環境,裡面沒有變數也沒有函數陳述句被提升 ![](https://i.imgur.com/m9AXL0k.png =80%x) <font color="#999999">圖片來源:udemy:JavaScript 全攻略:克服 JS 的奇怪部分 </font> 2. 執行到IIFE,會創造函數物件記憶體 ![](https://i.imgur.com/Hb6FYxu.png =80%x) <font color="#999999">圖片來源:udemy:JavaScript 全攻略:克服 JS 的奇怪部分 </font> 3. 接著看到 ('John'),呼叫函數的括號,一個新的執行環境就會被創造 ![](https://i.imgur.com/wNgb4F1.png =80%x) <font color="#999999">圖片來源:udemy:JavaScript 全攻略:克服 JS 的奇怪部分 </font> 4. 看到`var greeting = 'Hello'`,這個變數,會進到函數的執行環境中,==所以任何在裡面宣告的變數,都會在函數內部創造,不會接觸到全域環境==,這是個對寫程式很有用的方法 ![](https://i.imgur.com/UaQjbE5.png =80%x) <font color="#999999">圖片來源:udemy:JavaScript 全攻略:克服 JS 的奇怪部分 </font> 另一個例子: 如果我有兩個js檔案,greet.js 堆在 app.js 上面 ```htmlembedded <html> <head> </head> <body> <script src="greet.js"></script> <script src="app.js"></script> </body> </html> ``` 在greet.js 裡面有建立一個全域變數 `var greeting = 'Hola'` 而我們在app.js裡面的IIFE 裡的變數`greeting` 不會和greet.js裡的`greeting`產生衝突 ```javascript! // app.js (function (name) { const greeting = 'Hello' console.log( greeting + ' ' + name) // Hello John })('John') // 全域的greeting console.log(greeting) // Hola ``` 過程示意: 1. 程式被載入時,先建立全域執行環境,有變數greeting ![](https://i.imgur.com/g7v3LcC.png =80%x) <font color="#999999">圖片來源:udemy:JavaScript 全攻略:克服 JS 的奇怪部分 </font> 2. 執行到IIFE,並用()呼叫,建立自己的執行環境 ![](https://i.imgur.com/YANCke7.png =80%x) <font color="#999999">圖片來源:udemy:JavaScript 全攻略:克服 JS 的奇怪部分 </font> 3. 看到`var greeting = 'Hello'`,這個變數是被放在內部的,==兩個變數在不同的執行環境,在不同的記憶體位址==,可以保證 greeting 不會有衝突 ![](https://i.imgur.com/qZ0pQCk.png =80%x) <font color="#999999">圖片來源:udemy:JavaScript 全攻略:克服 JS 的奇怪部分 </font>