Jeff的隨手筆記

學習當一個前端工程師

0%

『JavaScript 基礎』 IIFE

Imgur

這是一門在Udemy 課程,是同學介紹的。主要是因為想要運用到過年前這段時間好好的增進自己的JavaScript的基礎能力,讓自己能往前端工程師更近一步。主要還是會以筆記的形式做呈現!


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

在之前有提到建立 function 的方法通常有 function statement 和 function expression 這兩種方式,
IIFEs 指的就是透過 function expression 的方式來建立函式,並且立即執行它。
如下列程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 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);

輸出結果為:

1
2
3
Hello John
Hello John
Hello John

前面兩段code在之前的章節我們都有學習到,但第三段code看起來很像是第二段code,只是多了點東西。其實這就是IIFE,用function expression的方式建立function後直接在最後面加上()。

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

Imgur

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

Imgur

但當我把function用()包起來並且再加上一個()後,他就會直接執行這個function裡的內容並回傳字串。

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

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

1
2
3
4
5
6
7
8
// using an Immediately Invoked Function Expression (IIFE)
var greeting = (function(name) {

return 'Hello ' + name;

})('John');

console.log(greeting());

我們會得到:

1
TypeError: greeting is not a function

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

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

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

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

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

1
SyntaxError: Function statements require a function name

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

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

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

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

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

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

1
2
3
4
5
6
7
8
var firstname = 'John';

(function(name) {

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

}(firstname));

這段我們要來探討,為什麼我們需要使用IIFEs?

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

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

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

1
2
3
4
5
6
(function(name) {

var greeting = 'Hello';
console.log(greeting + ' ' + name);

}('John'));

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

Imgur

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

Imgur

由於我們在function的最後有加上( ),所以這段function會立即被執行。JavaScript引擎會去逐行執行我們這個function中的程式碼內容,當它發現到我們的程式碼中建立了一個變數var greeting = 'Hello';,因此這個變數就被建立在這個execution context 中。

Imgur

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


在課堂的最後有提到,如果我要讓function execution這層的變數能夠同時影響到Global Execution Context的變數時,我該怎麼做?

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

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

1
2
3
4
5
6
7
8
9
10
var greeting = "Hola";
(function(global, name) {

var greeting = 'Hello';
global.greeting = 'Hello';
console.log(greeting + ' ' + name);

}(window, 'John'));

console.log(greeting);