詳解當即執行函數(function(){}()),(function(){})()

  要知道這幾種寫法之間的區別,咱們要先聊些題外話——js中函數的兩種命名方式,即表達式和聲明式。javascript

  函數的聲明式寫法爲:function foo(){/*...*/},這種寫法會致使函數提高,全部function關鍵字都會被解釋器優先編譯,不論是聲明在什麼位置,均可以調用它,可是它自己不會被執行,定義只是讓解釋器知道其存在,只有在被調用的時候纔會執行。java

圖1 聲明式函數瀏覽器

  函數的表達式寫法爲:var foo=function(){/*...*/},這種寫法不會致使函數提高,因而就必須先聲明,再調用,不然會出錯,如圖2。閉包

圖2 表達式函數函數

  如今,回到正題,(function(){}()),(function(){})()這兩種是js中當即執行函數的寫法,函數表達式後加上()能夠被直接調用,可是把整個聲明式函數用()包起來的話,則會被編譯器認爲是函數表達式,從而能夠用()來直接調用,如(function foo(){/*...*/})(),可是若是這個括號加在聲明式函數後面,如function foo(){/*...*/}(),則會報錯,不少博客說這種寫法()會被省略,但實際是會出錯,由於不符合js的語法,因此想要經過瀏覽器的語法檢查,就必須加點符號,好比()、+、!等,具體能夠查看圖3。spa

圖3 當即執行函數3d

 

 

 總結一下就是:code

function foo(){console.log("Hello World!")}()//聲明函數後加()會報錯
(function foo(){console.log("Hello World!")}())//用括號把整個表達式包起來,正常執行
(function foo(){console.log("Hello World!")})()//用括號把函數包起來,正常執行
!function foo(){console.log("Hello World!")}()//使用!,求反,這裏只想經過語法檢查。
+function foo(){console.log("Hello World!")}()//使用+,正常執行
-function foo(){console.log("Hello World!")}()//使用-,正常執行
~function foo(){console.log("Hello World!")}()//使用~,正常執行
void function foo(){console.log("Hello World!")}()//使用void,正常執行
new function foo(){console.log("Hello World!")}()//使用new,正常執行

  當即執行函數通常也寫成匿名函數,匿名函數寫法爲function(){/*...*/},就是使用function關鍵字聲明一個函數,但未給函數命名,假若須要傳值,直接將參數寫到括號內便可如圖4所示。blog

圖4 當即執行函數的傳參事件

  將它賦予一個變量則建立函數表達式,賦予一個事件則成爲事件處理程序等。可是須要注意的是匿名函數不能單獨使用,不然會js語法報錯,至少要用()包裹起來。上面的例子能夠寫成以下形式:

(function(){console.log("我是匿名函數。")}())
(function(){console.log("我是匿名函數。")})()
!function(){console.log("我是匿名函數。")}()
+function(){console.log("我是匿名函數。")}()
-function(){console.log("我是匿名函數。")}()
~function(){console.log("我是匿名函數。")}()
void function(){console.log("我是匿名函數。")}()
new function(){console.log("我是匿名函數。")}()

  當即執行函數的做用是:1.建立一個獨立的做用域,這個做用域裏面的變量,外面訪問不到,這樣就能夠避免變量污染。2.閉包和私有數據。提到閉包,不得不提下那道經典的閉包問題。

 1 <ul id=」test」>
 2     <li>這是第一條</li>
 3     <li>這是第二條</li>
 4     <li>這是第三條</li>
 5 </ul>
 6 
 7 <script>
 8     var liList=document.getElementsByTagName('li');
 9     for(var i=0;i<liList.length;i++)
10     {
11         liList[i].onclick=function(){
12             console.log(i);
13         }
14     };
15 </script>

  不少人以爲這樣的執行效果是點擊第一個li,則會輸出1,點擊第二個li,則會輸出二,以此類推。可是真正的執行效果是,無論點擊第幾個li,都會輸出3,如圖5所示。由於 i 是貫穿整個做用域的,而不是給每一個 li 分配了一個 i,用戶觸發的onclick事件以前,for循環已經執行結束了,而for循環執行完的時候i=3。

圖5 各自點擊第1,2,3個li,或是以後再次點了多少次,都會輸出3,可見,右邊控制檯輸出了8次3

  可是若是咱們用了當即執行函數給每一個 li 創造一個獨立做用域,就能夠改寫爲下面的這樣,這樣就能實現點擊第幾條就能輸出幾的功能。

 1 <script>
 2     var liList=document.getElementsByTagName('li');
 3     for(var i=0;i<liList.length;i++)
 4     {
 5         (function(ii) {
 6            liList[ii].onclick=function(){
 7                console.log(ii);
 8            }
 9        })(i)
10     };
11 </script>

  在當即執行函數執行的時候,i 的值被賦值給 ii,此後 ii 的值一直不變,如圖6所示。i 的值從 0 變化到 3,對應3 個當即執行函數,這 3個當即執行函數裏面的 ii 「分別」是 0、一、2。

圖6 點擊第幾個li,就輸出幾

 

   其實ES6語法中的let也能夠實現上述的功能,僅僅是將for循環中的var換成let,以下所示,有木有以爲很簡單明瞭。

1 <script>
2      var liList=document.getElementsByTagName('li');
3      for(let i=0;i<liList.length;i++)
4      {
5             liList[i].onclick=function(){
6                 console.log(i);
7              }
8      }
9 </script>

  那不少人就以爲用let能夠徹底取代當即執行函數,到目前爲止,多是我眼界所限制,我所能用到的當即執行函數的確能被let替代,前提是你的運行環境(包括舊的瀏覽器)支持ES2015。若是不支持,你將不得不求助於之前經典的函數。

相關文章
相關標籤/搜索