iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
自我挑戰組

重新複習JavaScript系列 第 19

[Day -19] IIFE

  • 分享至 

  • xImage
  •  

再繼續往下走之前我們先來認識一下什麼是IIFE?

第一次看到這個詞的時候,是在看youtube看**深入淺出 Javascript30** 的解說影片上得知的,但當時並不在意,想說課堂上沒教應該不是什麼重要的,誰知道現在又在**JavaScript: Understanding the Weird Part**上看到在教這個,看來是要來好好了解一下

什麼是IIFE?

IIFE,全稱Immediately Invoked Functions Expressions(立即執行函式)

在之前有提到建立 function 的方法通常有 function statement 和 function expression 這兩種方式,

IIFE 指的就是透過 function expression 的方式來建立函式,並且立即執行它。

如下列程式碼:

// function statement
function greet(name) {
    console.log('Hello ' + name);   
}
greet('John');

// using a function expression
var greetFunc = function(name) {
    console.log('Hello ' + name);
};
greetFunc('John');

// using an Immediately Invoked Function Expression (IIFE)
var greeting = (function(name) {
    
    return 'Hello ' + name;
    
})('John');

console.log(greeting);

輸出結果為:

Hello John
Hello John
Hello John

前面兩段code在之前的章節我們都有學習到,應該難不倒大家吧,但是第三段code看起來很像是第二段code但又多了點東西,讓我有點困惑。

其實這就是IIFE,用function expression的方式建立function後直接在最後面加上()。

我們用下面兩張圖片來說明會更加了解:

這張圖片就是我們之前學的function expression,我們可以看到,當我們console.log(greeting)出來的會是一個function。

既然知道前面這一段是一個function後,在正常的情況下,當我們今天要使用這個function就要使用「調用 (invoke)」,也就是再加上一個() 。也就是第一段跟第二段的code。

但如果是要使用IIFE的話,必須先把前面的function用()包起來,然後再加上「調用 (invoke)」,他就會直接執行這個function裡的內容並回傳字串。

另外這邊還有一個重點,在使用 IIFE 的寫法後,要記住他回傳的內容會是 字串 ,因此我們沒有辦法再次執行它。

如果我們強制在後面加上(),例如:

// using an Immediately Invoked Function Expression (IIFE)
var greeting = (function(name) {
    
    return 'Hello ' + name;
    
})('John');

console.log(greeting());

我們會得到:

TypeError: greeting is not a function

所以我們要特別注意這邊,避免出現錯誤(現在才知道我之前為什麼有時候會出現這個東西了)

更深入的應用 IIFE

在之前我們有提到過expression 的概念:輸入後能夠直接回傳值的一串程式,我們一般會把它存成一個變數,但是它不一定要被存成一個變數。

當如果想在function也做到,這時候我的第一個想法會是:

// ❌ 錯誤寫法
function(name) {
    var greeting = 'Inside IIFE: Hello';
    console.log(greeting + ' ' + name);
}

這時候我會得到一個錯誤:

SyntaxError: Function statements require a function name

之所以會錯誤是因為我用 function 做為開頭,所以JavaScript 引擎在解析程式碼的時候,它會認為你現在要輸入 function statement,但我卻沒有給這個function一個的名稱,於是它無法正確理解這段程式碼便拋出錯誤。

所以我們只要跟JavaScript 引擎說,這一整個並不是function statement就可以了。

(function(name) {
    var greeting = 'Inside IIFE: Hello';
    console.log(greeting + ' ' + name);
});

這是我們最常使用的做法:用括號 () 把 function(){ …} 包起來。

因為課堂上有說到,我們只會在括弧內放入 expression,例如 (3+2),而不會放 statement 在括弧內,所以JavaScript 就會以 expression 的方式來讀取這段函式。因此這個 function 會被建立,但是不會被存在任何變數當中,也不會被執行。

這時候再套用到剛剛學到的IIFE,在最後面加上()

var firstname = 'John';

(function(name) {
    
    var greeting = 'Inside IIFE: Hello';
    console.log(greeting + ' ' + name);
    
}(firstname));

為什麼我們需要使用IIFE?

還記得之前有提到的一個名詞:namespace。

namespace的使用是為了避免變項名稱重覆所造成的問題,而IIFE也很類似,他也是可以避免我們所建立的變項名稱因為覆蓋而造成影響。

我們就拿下面的範例來說明在JavaScript引擎實際發生了什麼:

(function(name) {
    
    var greeting = 'Hello';
    console.log(greeting + ' ' + name);
    
}('John'));

當我執行剛剛那段code時,會先建立Global Execution Context(裡面是空的,因為我們沒有設立變數)

接下來JavaScript引擎會執行這段IIFE,它會將這個匿名函式儲存在Execution Context中。

由於我們在function的最後有加上( ),所以這段function會立即被執行。

JavaScript引擎會去逐行執行我們這個function中的程式碼內容,當它發現到我們的程式碼中建立了一個變數var greeting = 'Hello';,因此這個變數就被建立在這個execution context 中。

因此,透過IIFE,我們可以發現,在IIFE中所建立的變數,都不會影響到Global Execution Context所建立的變數,也就是說,透過IIFE,它避免了我們的變數間可能會互相干擾覆蓋的情況。


那如果我要讓function execution這層的變數能夠同時影響到Global Execution Context的變數時,我該怎麼做?

其實也是之前有提到的一個觀念:by reference

由於我們知道物件是by reference的特性,因此我們只要把全域變數帶入就可以了,因此我們必須要多一個參數,叫做global,在帶入參數的地方放入window(全域變數),之後再直接針對window裡面的物件去做改變。如下:

var greeting = "Hola";
(function(global, name) {
    
    var greeting = 'Hello';
    global.greeting = 'Hello';
    console.log(greeting + ' ' + name);
    
}(window, 'John'));

console.log(greeting);

我又發現鐵人賽的一個困難點了,排每天主題!!!

開賽前都不覺得這會是問題,但實際開始寫後發現這是一個很難的問題XD

就例如今天的主題,我非常猶豫要不要先講Event Loop,畢竟上一篇是講Event 剛好名字有相關(喂~~)。但講Event Loop又會帶到callback function,講到這個又會提到closures…一環扣著一環啊,最後我放棄選擇了,我決定用我之前上JavaScript: Understanding the Weird Part所教的順序來寫,這樣我的內容才會比較有條理一點吧><”


上一篇
[Day -18] 事件機制的原理
下一篇
[Day -20] Closures
系列文
重新複習JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言