前面的閉包中,提到與閉包類似的當即執行函數,感受二者仍是比較容易弄混吧,嚴格來講(由於犀牛書和高程對閉包的定義不一樣),當即執行函數並不屬於閉包,它不知足閉包的三個條件。javascript
圓括號運算符也叫分組運算符,它有兩種用法:若是表達式放在圓括號中,做用是求值;若是跟在函數後面,做用是調用函數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
使用 function
關鍵字建立一個函數,而且後面帶有函數名,叫函數聲明。token
function testa(){}
那麼使用 function
關鍵字建立的函數不帶函數名呢? 那就是匿名函數了。ip
function (){}
那麼把匿名函數賦值給一個變量呢?那就是函數表達式了。作用域
var testa = function (){}
其實呢,函數表達式的根本所在,就是阻止了js引擎把 用function
建立的函數 看成函數聲明來解析。下面再詳說。io
那麼當即執行函數呢?
用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")}()
都是均可以把函數聲明 轉爲 函數表達式,也就是阻止了把其看成函數聲明解析。
一、當即執行函數能配合閉包保存狀態。
來看下 上節內容中閉包的例子:
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"
首先當即執行函數,它是個匿名函數,你是得不到它的函數引用,這樣,就避免了全局變量污染。其次,因爲函數做用域的規則,在匿名函數外部是訪問不了函數內的變量,函數等的。因此,也常常用當即執行函數模擬塊級做用域。