就是下面這樣的的代碼:
(function foo(){
console.log( "Hello!" );
})();
關于IIFE書寫方式
第一種情況:
function(){ /* code */ }(); // SyntaxError: Unexpected token (
解釋:JavaScript在解析代碼時,當遇到function
關鍵字時,會默認把它當做一個函數聲明,而不是函數表達式,如果沒有把它顯式的寫成函數表達式,就報錯,因為函數聲明需要一個函數名。上面的報錯是因為沒有函數名(第一個左括號)。
第二種情況:
function foo(){ /* code */ }(); // SyntaxError: Unexpected token )
解釋:在表達式后面加括號表示執行;此外,在函數聲明一個語句,在語句后面加括號可等價為:
function foo(){ /* code */ }
(); // SyntaxError: Unexpected token )
相當于聲明了函數foo,后面進行()
的表達式操作。()
表示分組操作符,內部表達式不能為空,所以報錯,上面的報錯是因為分組操作符內為空的錯誤。
第三種情況:
(function(){ /* code */ }());
為什么這樣就能立即執行并且不報錯呢?因為在javascript里,括號內部不能包含語句,當解析器對代碼進行解釋的時候,先碰到了()
,然后碰到function關鍵字就會自動將()
里面的代碼識別為函數表達式而不是函數聲明。
因此,也可以這么寫:
(function(){ /* code */ })();
此外,只要功能類似于()
將語句轉化為表達式的方式都能用于IIFE,例如下面的寫法都會生效:
false || function () {console.log('hello')}()
true && function () {console.log('hello')}()
'anything', function () {console.log('hello')}()
~function () {console.log('hello')}()
!function () {console.log('hello')}()
+function () {console.log('hello')}()
-function () {console.log('hello')}()
~(function () {console.log('hello')})() // 變種
第四種情況:
var i = function(){ return 10; }();
這樣的寫法把fucntion
當做函數表達式,可以不加()
,但是為了增加代碼可讀性,建議加上。
和閉包的配合
1. 保存函數內部變量狀態
下面代碼并不會像你想象那樣的執行,因為i
的值沒有被鎖住,當我們點擊鏈接的時候,其實for循環已經執行完了,于是在點擊的時候i的值其實已經是elems.length了。
var elems = document.getElementsByTagName( 'a' );
for ( var i = 0; i < elems.length; i++ ) {
elems[ i ].addEventListener( 'click', function(e){
e.preventDefault();
alert( 'I am link #' + i );
}, 'false' );
}
這樣改寫:
var elems = document.getElementsByTagName( 'a' );
for ( var i = 0; i < elems.length; i++ ) {
(function( i ){
// 其實代碼中的 i 和外面的 i 是在不同的作用域里完全不同,比如:
elems[ i ].addEventListener( 'click', function(e){
e.preventDefault();
console.log( 'I am link #' + i );
}, 'false' );
})( i );
}
IIFE代碼中的i
和外面的i
是在不同的作用域里,完全不同的兩個值,只是名字一樣。
2. 局部變量、模塊化
立即執行函數在模塊化中也大有用處。用立即執行函數處理模塊化可以減少全局變量造成的空間污染,構造更多的私有變量。
// 創建一個立即執行的匿名函數
// 該函數返回一個對象,包含你要暴露的屬性
// 如下代碼如果不使用立即執行函數,就會多一個屬性i
// 如果有了屬性i,我們就能調用counter.i改變i的值
// 對我們來說這種不確定的因素越少越好
var counter = (function(){
var i = 0;
return {
get: function(){
return i;
},
set: function( val ){
i = val;
},
increment: function() {
return ++i;
}
};
}());
// counter其實是一個對象
counter.get(); // 0
counter.set( 3 );
counter.increment(); // 4
counter.increment(); // 5
counter.i; // undefined i并不是counter的屬性
i; // ReferenceError: i is not defined (函數內部的是局部變量)
總結
- 能轉化為函數表達式的操作都能構造IIFE
- IIFE和普通函數類似,可以傳參,有返回值
- IIFE可以制造局部變量,用于模塊化