iT邦幫忙

5

[筆記][JavaScript]關於即刻執行函式(immediately invo....IIFE)的用法

嗨啊啊啊,首先我要先道歉一下,感覺我很多觀念都還沒打穩,所以打文章的時候並沒有講解的很清楚,只是想說如果試著去理解,並且打出來和大家分享應該會得到更厲害的大大的建議,當然也是真的有啦!但總覺得如果是這樣有點不太好意思,雖然把他歸類成筆記,但是既然文章出來了,也是一種技術分享,就覺得應該要更認真的去做好這件事情才行。

另外我也在板上另一個大大fysh711426的文章[Google Code Jam] 2018 資格賽中答應說也要試著來解一下問題,關於這個我原本想說用JavaScript來處理掉,但是現在還沒有去處理到這部分,哦怎麼覺得從客戶那欠東西欠到這裡了/images/emoticon/emoticon16.gif,不過相信我,我一定會找時間把它處理掉的,如果板上其他大大有興趣也可以去看一下問題在這裡!啊啊啊現在先不要點進去,這篇文章的主題都還沒開始,那題外話就不再多說,以下開始!

關於即刻執行函數(immediately invoked function expression),這個不管是中文或是英文都很長,所以大家都簡稱IIFE,他的意思就如同字面,就是撰寫完函式內容後,直接呼叫執行該函數,以下例子:

//一個一般的函式會是以下這樣
var funcA = function(){
    console.log('hello!');
};
//去執行他
funcA();  //會在console中印出'hello!'

//而IIFE看起來會是以下這樣
(function funcB(){ //IIFE的開頭
    console.log('hello!') //函式內執行的內容
}()); //IIFE的結尾
//不需要特別去執行就會直接在console中印出hello!

funcB();  //但是當我們去做呼叫時會出現「funcB is not defined」的錯誤,因為在IIFE開頭的括號是代表這段程式碼是作為一個述句執行裡面的運算式,並不是以function去宣告函式,所以他也不會被存在全域變數中,也就不可能呼叫的到了。

接下來繼續改寫IIFE的內容,IIFE接在函式後右大括號的兩個小括號會讓函式即刻被執行,也就是說他是靠那個小括號去呼叫的,所以我們可以利用他去加入參數:

//IIFE可以是匿名函式(就是不幫函式取名字),因為他馬上就會執行了,之後也不需要再呼叫他,所以有沒有名字都無所謂。
(function(str){   //我們在這裡加入str用來裝執行時傳入的參數。
    console.log(str);
}('hello'));   //我們把字串'hello'傳入IIFE中
//不需要執行便會直接在console中印出hello

再來我們看一下IIFE的奇行種/images/emoticon/emoticon37.gif,上面第一個例子的最後有提到說,開頭的括號是作為一個述句宣告,為了能讓裡面的程式碼變成運算式執行,所以說其實只要能夠讓該匿名函式變成運算式都能夠當成IIFE來使用!例如以下:

//function和上面例子一樣,不同的是把原本包著他的括號拿掉,並在前面放一個!運算子
!function(str){   
    console.log(str);
}('hello'); 
//登愣!會發現他還是會在console中印出hello,但是他會回傳個結果true,因為!運算子會回傳boolean的值,而該函式沒有return東西也就是undefined,但是!是Not運算子,所以會把結果從undefined的false轉成ture回傳,好,這是有點長的題外話,那我們繼續看下去。

//如果他本身就是在運算子的環境中,甚至不需要再把他變成運算子,例如:
var funcA = 
    function(str){   
        console.log(str);
    }('hello'); 
//一樣會直接在console中印出hello!

好的,那我們講了那麼多IIFE,但是他到底可以用在什麼地方呢?大家應該都知道JavaScript的變數範圍有分成全域和區域,IIFE就是為了讓全域變數的環境不被全域變數汙染,和在區域中,建立一個新的環境,避免在同個區域內共用同個變數。

讓我們看看以下在全域環境使用的例子:

//先建立幾個簡單的函式
function writeStr(str){
    console.log(str);
};
function setValue(){
    return 'A'
};
//在全域中使用
var strA = setValue();  //設定strA的值
writeStr(strA);  //把strA的值交給函式writeStr印出到console

strA; //會回傳'A'因為他已經被存在window,也就是全域環境中了

//雖然以上這麼做是沒問題的,但是全域環境會增加一個strA,這對於依賴全域環境做執行的JavaScript容易出現一些問題,所以我們應該要避免汙染到全域變數,如下:
//建立IIFE
(function(){
    var strB = setValue();   //在IIFE中的匿名函式中宣告該變數,他的範圍就只會在這個IIFE中,不會汙染到全域環境。
    writeStr(strB);  //一樣把strA丟到函式writeStr印到console上
}());

strB; //會出現strB is not defined的錯誤,全域變數沒被汙染

最後是在區域中使用的例子:

//建立一個一般的函式,並觀察temp在各個地方的變化
function funcA(str){
    var temp = '';
    console.log('(1):' + temp);
    if(str !== ''){
        var temp = str;
        console.log('(2):' + temp);
    }
    console.log('(3):' + temp);
};

funcA('hello');  
//執行後會在console上印出:
//(1):''
//(2):hello
//(3):hello
//會發現在if內宣告的同名變數temp會在if中設定新的值後,會連整個函式funcA原本的temp值都改變了。

//接著在函式內使用IIFE,再觀察temp的變化
function funcB(str){
    var temp = '';
    console.log('(1):' + temp);
    if(str !== ''){
        (function(){
        var temp = str;
        console.log('(2):' + temp);}());
    }
    console.log('(3):' + temp);
};

funcB('hello');
//執行後會在console上印出:
//(1):''
//(2):hello
//(3):''
//可以看到temp這個變數在IIFE中被改變的內容並不會遺留到IIFE外的任何地方,這樣就可以避免在同個環境執行時,共用同個變數名稱會影響到原本的內容。

以上是對IIFE的一些說明和應用,如果有說明錯誤或不明白的地方,麻煩再留言告知我,我會盡速改正!!謝謝大家!!


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
小碼農米爾
iT邦高手 1 級 ‧ 2018-04-15 12:51:31

我相信你,等你的 JavaScript 解答出爐 /images/emoticon/emoticon01.gif

0
tacodrem
iT邦新手 5 級 ‧ 2018-05-31 17:44:46

請問如果是區域變數的污染問題的話, 是否使用ES6的let宣告, 就可以避免了呢?

神Q超人 iT邦研究生 5 級 ‧ 2018-05-31 20:04:25 檢舉

對哦!就像以下例子一樣,因為let就是宣告一個區域變數,所以他們在各自的區域內不會互相影響,甚至在全域中直接以let去宣告的變數,該變數也不會出現在window中,所以就連全域的部分也不用擔心:

function funcC(str){
    let temp = '';
    console.log('(1):' + temp);
    if(str != ''){
        let temp = str;
        console.log('(2):' + temp);
    };
    console.log('(3):' + temp);
};

funcC('hello!');
//會印出以下結果
//(1):''
//(2):hello!
//(3):''

但是但是但是,因為很重要所以說了三次,目前ES6的let支援度,必須要IE11以上才可以使用,不然就會出錯,不過如果確定客戶群全都使用chrome就沒關係了/images/emoticon/emoticon42.gif

tacodrem iT邦新手 5 級 ‧ 2018-06-04 09:08:48 檢舉

喔喔~!!
還好目前是無視IE的情況XDD

P.S
請問一下, 上面的範例, if裡面是怎麼印出hello!的@@?
str並沒有把值給temp不是?

神Q超人 iT邦研究生 5 級 ‧ 2018-06-04 11:40:31 檢舉

哈哈哈,對因為我忘記打了,抱歉!

話說能夠無視IE真的很幸福XD

我要留言

立即登入留言