立即執行函式IIFE
阿新 • • 發佈:2019-02-20
到底什麼是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);
}
})