一:什麼是立即執行函數?
聲明一個函數,並馬上調用這個匿名函數就叫做立即執行函數;也可以說立即執行函數是一種語法,讓你的函數在定義以後立即執行;
立即執行函數的創建步驟,看下圖:
image.png
二:立即執行函數的寫法:
有時,我們定義函數之後,立即調用該函數,這時不能在函數的定義後面直接加圓括號,這會產生語法錯誤。產生語法錯誤的原因是,function 這個關鍵字,既可以當做語句,也可以當做表達式,比如下邊:
//語句
function fn() {};
//表達式
var fn = function (){};
爲了避免解析上的歧義,JS引擎規定,如果function出現在行首,一律解析成語句。因此JS引擎看到行首是function關鍵字以後,認爲這一段都是函數定義,不應該以原括號結尾,所以就報錯了。
解決方法就是不要讓function出現在行首,讓JS引擎將其理解爲一個表達式,最簡單的處理就是將其放在一個圓括號裏,比如下邊:
(function(){
//code
}())
(function (){
//code
})()
上邊的兩種寫法,都是以圓括號開頭,引擎會意味後面跟的是表達式,而不是一個函數定義語句,所以就避免了錯誤,這就叫做"立即調用的函數表達式"。
立即執行函數,還有一些其他的寫法(加一些小東西,不讓解析成語句就可以),比如下邊:
(function () {alert("我是匿名函數")}()) //用括號把整個表達式包起來
(function () {alert("我是匿名函數")})() //用括號把函數包起來
!function () {alert("我是匿名函數")}() //求反,我們不在意值是多少,只想通過語法檢查
+function () {alert("我是匿名函數")}()
-function () {alert("我是匿名函數")}()
~function () {alert("我是匿名函數")}()
void function () {alert("我是匿名函數")}()
new function () {alert("我是匿名函數")}()
三:立即執行函數的作用:
- 不必爲函數命名,避免了污染全局變量
- 立即執行函數內部形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量
- 封裝變量
總而言之:立即執行函數會形成一個單獨的作用域,我們可以封裝一些臨時變量或者局部變量,避免污染全局變量
四:與立即執行函數相關的面試題:
<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>
五、立即執行函數使用的場景
1、你的代碼在頁面加載完成之後,不得不執行一些設置工作,比如時間處理器,創建對象等等。
2、所有的這些工作只需要執行一次,比如只需要顯示一個時間。
3、但是這些代碼也需要一些臨時的變量,但是初始化過程結束之後,就再也不會被用到,如果將這些變量作爲全局變量,不是一個好的注意,我們可以用立即執行函數——去將我們所有的代碼包裹在它的局部作用域中,不會讓任何變量泄露成全局變量,看如下代碼:
image.png
比如上面的代碼,如果沒有被包裹在立即執行函數中,那麼臨時變量todaydom,days,today,year,month,date,day,msg都將成爲全局變量(初始化代碼遺留的產物)。用立即執行函數之後,這些變量都不會在全局變量中存在,以後也不會其他地方使用,有效的避免了污染全局變量。
六、立即執行函數的參數
(function(j){
//代碼中可以使用j
})(i)
如果立即執行函數中需要全局變量,全局變量會被作爲一個參數傳遞給立即執行函數(上例中的i就是一個全局變量,i代表的是實參,j是i在立即執行函數中的形參)。
七、立即執行函數的返回值
像其他函數一樣,立即執行函數也可以有返回值。除了可以返回基本類型值以外,立即執行函數也能返回任何類型的值,比如對象,函數。
image.png
上例中立即執行函數的返回值被賦值給了一個變量result,這個函數簡單的返回了res的值,這個值事先被計算並被存儲在立即執行行數的閉包中。
在五中,如果在以後的代碼中我需要msg這個值,我也可以返回一個包含msg的對象,方便在以後代碼中使用(這樣五中的一些臨時變量也沒有暴露在外面)。
八、總結立即執行函數有哪些作用?
1、改變變量的作用域(創建一個獨立的作用域)
<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>
改變變量i的作用域,把全局變量i以參數的形式傳遞到立即執行函數中,在立即執行函數中定義變量i的形參變量j,變量j就是在立即執行函數的作用域中。(給每個li創建了一個作用域塊,點擊的時候尋找自由變量j,在立即執行塊中找到)
2、封裝臨時變量
image.png
在上面的代碼中,可以封裝臨時變量,避免全局變量的污染。也可以返回一個在全局中需要的變量(用return)。
作者:泡杯感冒靈
鏈接:https://www.jianshu.com/p/b10b6e93ddec
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。