進擊的 JavaScript(五) 之 當即執行函數與閉包

前面的閉包中,提到與閉包類似的當即執行函數,感受二者仍是比較容易弄混吧,嚴格來講(由於犀牛書和高程對閉包的定義不一樣),當即執行函數並不屬於閉包,它不知足閉包的三個條件。javascript


1、圓括號運算符

  圓括號運算符也叫分組運算符,它有兩種用法:若是表達式放在圓括號中,做用是求值;若是跟在函數後面,做用是調用函數java

  把表達式放在圓括號之中,將返回表達式的值閉包

console.log((1+2)); // 3

  將函數放在圓括號中,會返回函數自己。若是圓括號緊跟在函數的後面,就表示調用函數,即對函數求值模塊化

console.log((function testa(){return 666;}));
// function testa(){return 666;}

console.log(function testa(){return 666;}());
// 666

注意:圓括號運算符不能爲空,不然會報錯函數

();//SyntaxError: Unexpected token )

因爲圓括號的做用是求值,若是將語句放在圓括號之中,就會報錯,由於語句沒有返回值code

(var a = function(){return 666});
// SyntaxError: Unexpected token var


2、函數聲明

使用 function 關鍵字建立一個函數,而且後面帶有函數名,叫函數聲明。token

function testa(){}


3、匿名函數

那麼使用 function 關鍵字建立的函數不帶函數名呢? 那就是匿名函數了。ip

function (){}


4、函數表達式

那麼把匿名函數賦值給一個變量呢?那就是函數表達式了。作用域

var testa = function (){}

其實呢,函數表達式的根本所在,就是阻止了js引擎把 function建立的函數 看成函數聲明來解析。下面再詳說。io


5、當即執行函數(IIFE)

那麼當即執行函數呢?

function定義函數以後,當即調用該函數。這種函數就叫作當即執行函數,全稱爲當即調用的函數表達式IIFE(Imdiately Invoked Function Expression)

一、在本系列進擊的 JavaScript(三)中到過,代碼執行時,會先對函數聲明的函數 進行解析(函數聲明提高),而函數表達式,當逐行執行到它時,纔會解析。

二、正由於函數聲明的提高,致使函數聲明不能當即執行。由於,函數聲明時,js只會解析到大括號(})就結束了,若是後面有(),只是一個圓括號運算符。

function testa(){
    console.log("testa")
}("666")

//"666"
//若是後面是一個空的圓括號,會報錯,上面提到過。

因此,不知道,你有沒有發現,函數聲明的函數,後面能夠不用分號(;)分隔,也能夠正常執行,而函數表達式的後面就必須加分號,否則會報錯。你能夠本身寫個小栗子驗證下。

三、匿名函數是不能單獨寫的,因此就提不上當即執行了。

function (){}
//Uncaught SyntaxError: Unexpected token (

單獨寫匿名函數,是會報錯的,js引擎 會把它看成函數聲明來解析,而函數聲明就必需要有個函數名,因此會報錯。
因此,你一般看到使用匿名函數,都是看成參數傳遞的,或者把匿名函數轉爲函數表達式。

四、所以,只有函數表達式能夠當即執行

var testa = function (){
    console.log("testa")
}()
//"testa"

上面提到過,函數表達式,就是阻止了js引擎把 |用function建立的函數| 看成函數聲明來解析。

注:javascript引擎規定,若是function關鍵字出如今行首,一概解釋成函數聲明語句。

因此,解決方法就是不要讓function出如今行首,讓引擎將其理解成一個表達式。

//經常使用的兩種,使用圓括號運算符
(function(){console.log("666")})()
(function(){console.log("666")}())

//一元運算符寫法
!function(){console.log("666")}()
+function(){console.log("666")}()
-function(){console.log("666")}()
~function(){console.log("666")}()

都是均可以把函數聲明 轉爲 函數表達式,也就是阻止了把其看成函數聲明解析。


6、當即執行函數在閉包中的應用

一、當即執行函數能配合閉包保存狀態。

來看下 上節內容中閉包的例子:

function makeClosures(i){    
    var i = i;  
    return function(){
        console.log(i);     
    }
}

for (var i=1; i<=5; i++) {
    setTimeout(makeClosures(i),i*1000);  
}
//1
//2
//3
//4
//5

如今,咱們來利用當即執行函數來簡化它:

for (var i=1; i<=5; i++) {
    setTimeout((function(i){
        return function(){
            console.log(i);
        }
    })(i),i*1000);
}

第一個匿名函數執行完畢後,返回了第二個匿名函數。第二個匿名函數被當作setTimeout 的第一個參數傳入進去。由於 setTimeout函數執行了5次,因此當即執行函數裏每次都會返回了一個沒有被執行的匿名函數,(這裏就是返回了5個匿名函數),每一個匿名函數內部保存着每次傳進來的i值,所以,每一個i 都是不同的,因此,就獲得了想要的結果

二、當即執行函數配合閉包 模塊化中應用

(function(){
    var meg = "hello zdx";
    
    function say(arg){
        arg = arg || meg;
        console.log(arg)
    }
    
    window.say = say;
})(window)

window.say();

//"hello zdx"

首先當即執行函數,它是個匿名函數,你是得不到它的函數引用,這樣,就避免了全局變量污染。其次,因爲函數做用域的規則,在匿名函數外部是訪問不了函數內的變量,函數等的。因此,也常常用當即執行函數模擬塊級做用域。

相關文章
相關標籤/搜索