一、什么是立即執行函數?
聲明一個函數,並馬上調用這個匿名函數就叫做立即執行函數;也可以說立即執行函數是一種語法,讓你的函數在定義以后立即執行;
立即執行函數的創建步驟,看下圖:
二、立即執行函數的寫法:
有時,我們定義函數之后,立即調用該函數,這時不能在函數的定義后面直接加圓括號,這會產生語法錯誤。產生語法錯誤的原因是,function 這個關鍵字,既可以當做語句,也可以當做表達式,比如下邊:
//語句
function fn() {}; //表達式
var fn = function (){};
為了避免解析上的歧義,JS引擎規定,如果function出現在行首,一律解析成語句。因此JS引擎看到行首是function關鍵字以后,認為這一段都是函數定義,不應該以原括號結尾,所以就報錯了。
解決方法就是不要讓function出現在行首,讓JS引擎將其理解為一個表達式,最簡單的處理就是將其放在一個圓括號里,比如下邊:
解決方法就是不要讓function出現在行首,讓JS引擎將其理解為一個表達式,最簡單的處理就是將其放在一個圓括號里,比如下邊:
(function(){ //code
}()) (function (){ //code
})()
上邊的兩種寫法,都是以圓括號開頭,引擎會意味后面跟的是表達式,而不是一個函數定義語句,所以就避免了錯誤,這就叫做"立即調用的函數表達式"。
立即執行函數,還有一些其他的寫法(加一些小東西,不讓解析成語句就可以),比如下邊:
立即執行函數,還有一些其他的寫法(加一些小東西,不讓解析成語句就可以),比如下邊:
(function () {alert("我是匿名函數")}()) //用括號把整個表達式包起來
(function () {alert("我是匿名函數")})() //用括號把函數包起來
!function () {alert("我是匿名函數")}() //求反,我們不在意值是多少,只想通過語法檢查
+function () {alert("我是匿名函數")}() -function () {alert("我是匿名函數")}() ~function () {alert("我是匿名函數")}() void function () {alert("我是匿名函數")}() new function () {alert("我是匿名函數")}()
三、立即執行函數的作用:
- 不必為函數命名,避免了污染全局變量
- 立即執行函數內部形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量
- 封裝變量
總而言之:立即執行函數會形成一個單獨的作用域,我們可以封裝一些臨時變量或者局部變量,避免污染全局變量
四、使用場景
1.怎樣使以下alert的結果為0,1,2:
<body>
<ul id="list">
<li>公司簡介</li>
<li>聯系我們</li>
<li>營銷網絡</li>
</ul>
<script>
var list = document.getElementById("list"); var li = list.children; for(var i = 0 ;i<li.length;i++){ li[i].onclick=function(){ alert(i); // 結果總是3.而不是0,1,2
} } </script>
</body>
為什么alert總是3? 因為i是貫穿整個作用域的,而不是給每一個li分配一個i,點擊事件使異步,用戶一定是在for運行完了以后,才點擊,此時i已經變成3了。
那么怎么解決這個問題呢,
那么怎么解決這個問題呢,
可以用立即執行函數,給每個li創建一個獨立的作用域
,在立即執行函數執行的時候,i的值從0到2,對應三個立即執行函數,這3個立即執行函數里邊的j分別是0,1,2所以就能正常輸出了,看下邊例子:
<body>
<ul id="list">
<li>公司簡介</li>
<li>聯系我們</li>
<li>營銷網絡</li>
</ul>
<script>
var list = document.getElementById("list"); var li = list.children; for(var i = 0 ;i<li.length;i++){ ( function(j){ li[j].onclick = function(){ alert(j); })(i); //把實參i賦值給形參j } } </script>
</body>
當然,也可以使用ES6的塊級作用域解決整個問題:
<body>
<ul id="list">
<li>公司簡介</li>
<li>聯系我們</li>
<li>營銷網絡</li>
</ul>
<script>
var list = document.getElementById("list"); var li = list.children; for(let i = 0 ;i<li.length;i++){ li[i].onclick=function(){ alert(i); // 結果是0,1,2
} } </script>
</body>
2.如何避免了污染全局變量
某些代碼只需要執行一次,比如只需要顯示一個時間,但是這些代碼也需要一些臨時的變量,但是初始化過程結束之后,就再也不會被用到,如果將這些變量作為全局變量,不是一個好的主意,我們可以用立即執行函數——去將我們所有的代碼包裹在它的局部作用域中,不會讓任何變量泄露成全局變量,看如下代碼:
比如上面的代碼,如果沒有被包裹在立即執行函數中,而是直接以非函數的形式直接寫在<script>標簽里面,雖然也會立即執行,但是臨時變量todaydom,days,today,year,month,date,day,msg都將成為全局變量(初始化代碼遺留的產物)。
而用立即執行函數之后,這些變量都不會在全局變量中存在,以后也不會其他地方使用,有效的避免了污染全局變量。