1. 程式人生 > >立即執行函式IIFE

立即執行函式IIFE

到底什麼是IIFE?

比較以下兩種方式

var foo=function(){ /*code*/}
foo();

另一種錯誤的方法:

function(){}(); //Unexpected token

報錯原因

JS程式碼解釋的時候,遇到function關鍵字會預設把它當做一個函式宣告,而不是函式表示式,如果沒有把它顯示錶達成函式表示式就會報錯。 沒有函式名,所以會報錯

加上函式名

function foo(){}() //unexpected token

加上了名字仍然報錯,我們在一個表示式後面加上括號表示這個表示式立即執行。
但如果是一個語句後面加括號,則前後沒有關係。以上程式碼等價於:

function foo(){}
();

相當於先聲明瞭一個叫foo的函式,之後進行()內的表示式運算,但是()(分組操作符)內的表示式不能為空,所以報錯。

IIFE正解

(function(){ /* code */ }());

成功,為什麼? JS中,括號內部不能包含語句,當直譯器對程式碼進行解釋時,先碰到了(),然後碰到function關鍵字就會自動將()裡面的程式碼識別為函式表示式而不是函式宣告~~

IIFE其他更多的寫法:

//常用寫法
(function(){}());
(function(){})();

//括號 和 JS的一些操作符如(= && || , 等)可以在函式表示式和函式宣告上消除歧義
//但是這種寫法你不能交換兩個的位置,直譯器如果是先遇到function關鍵字就會報錯了。 var i=fuction(){return 10;}(); true&&function(){/*code*/}(); 0,function(){}();

一元運算子

!function(){}();
~function(){}();
-function(){}();
+function(){}();

new操作符

new function(){}
new function(){}() //帶引數

通過以上的介紹,我們大概瞭解通過()可以使得一個函式表示式立即執行。
有的時候,我們實際上不需要使用()使之變成一個函式表示式,啥意思?比如下面這行程式碼,其實不加上()也不會保錯:

var i = function(){ return 10; }();

道理還是一樣,因為直譯器先遇到=號了,就把後面的預設當做表示式處理了

但是好的程式碼規範

仍然是需要加上括號

var i = (function(){ return 10; }());

為了程式碼的可讀性。

IIFE與閉包

IIFE可以配合閉包儲存狀態
在IIFE裡面再定義一個函式,這個函式能夠引用IIFE內部的變數和引數,利用這一點我們就可以利用IIFE鎖住變數的狀態了。

//執行後,我們得到了想要的結果,就是因為IIFE把迴圈變數的每個i都鎖在記憶體中了,
//儘管for迴圈結束後i的值已經變了,但是閉包的原因,其實記憶體中還是有對這些變數的引用存在,就是說有副本存在。
var elems=document.getElementsByTagName( 'a' );
for ( var i = 0; i < elems.length; i++ ) {
    (function(lockedIndex){
        elems[i].addEventlistener('click',function(e){
            e.preventDefault();
            console.log('i am '+lockedIndex);
        },'false');
    })(i)
}

其實上面的程式碼中的lockedIndex換成i也是可以的,因為是在兩個作用域(形參在內部函式作用域,而傳入的i在外部IIFE的作用域)

函式宣告和函式提升

有一種值得我們注意的做法:

if(condition){
    function sayHi(){
        alert('Hi');
    }
}else{
    function sayHi(){
        //other code
    }
}

這麼做是有問題的,在ECMAScript中是無效語法,JS引擎會修改這個錯誤,轉換成合理的狀態。 但問題是瀏覽器嘗試修正錯誤的做法並不一致。
大多數瀏覽器返回第二個宣告,直接忽略condition

但是如果使用函式表示式,那麼就沒問題:

if(condition){
    sayHi=function (){
        alert('Hi');
    }
}else{
    sayHi=function (){
        //other code
    }
}

應該使用命名函式表示式,而不是callee

//arguments.callee 在嚴格模式下不通過
function factoral(num){
    if(num<=1){
        return 1;
    }else{
        return num*arguments.callee(num-1);
    }
}
var factoral=(function f(num){
    if(num<=1){
        return 1;
    }else{
        return num*f(num-1);
    }
})