什麼是立即執行函數,它有什麼作用?

一:什麼是立即執行函數?

聲明一個函數,並馬上調用這個匿名函數就叫做立即執行函數;也可以說立即執行函數是一種語法,讓你的函數在定義以後立即執行;

立即執行函數的創建步驟,看下圖:

 

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("我是匿名函數")}() 

三:立即執行函數的作用:

  1. 不必爲函數命名,避免了污染全局變量
  2. 立即執行函數內部形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量
  3. 封裝變量

總而言之:立即執行函數會形成一個單獨的作用域,我們可以封裝一些臨時變量或者局部變量,避免污染全局變量

四:與立即執行函數相關的面試題:

 

<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
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章