JS 立即執行函數可以讓函數在創建后立即執行,這種模式本質上就是函數表達式(命名的或者匿名的),在創建后立即執行。
1、立即執行函數的寫法
立即執行函數通常有下面兩種寫法:
//第一種寫法 (function(){ ... })(); //第二種寫法 (function(){ ... }()); //錯誤的寫法 function (){ ... }(); //報錯: Uncaught SyntaxError: Unexpected token (
第三種寫法報錯的原因是,Javascript引擎看到function關鍵字之后,認為后面跟的是函數定義語句,而在一條語句后面加上() 會被當做分組操作符,分組操作符里必須要有表達式,所以這里報錯,不應該以圓括號結尾。以圓括號開頭,引擎就會認為后面跟的是一個表示式,而不是函數定義,所以就避免了錯誤。
讓Javascript引擎認為這是一個表達式的方法還有很多:
!function(){}(); +function(){}(); -function(){}(); ~function(){}(); new function(){ /* code */ } new function(){ /* code */ }() // 只有傳遞參數時,才需要最后那個圓括號
2、立即執行函數的作用
立即執行函數只有一個作用:創建一個獨立的作用域。這個作用域里面的變量,外面訪問不到(即避免了「變量污染」)。
面試題:
var liList = ul.getElementsByTagName('li') for(var i=0; i<6; i++){ liList[i].onclick = function(){ alert(i) // 為什么 alert 出來的總是 6,而不是 0、1、2、3、4、5 } }
因為輸出的 i 是全局作用域的,當循環結束后 i 的值是 6,所以輸出的 i 就是6。
用立即執行函數可以解決這個問題。
var liList = ul.getElementsByTagName('li') for(var i=0; i<6; i++){ (function(j){ liList[j].onclick = function(){ alert(j) // 0、1、2、3、4、5 } })(i) }
因為 JS 中調用函數傳遞參數都是值傳遞 ,所以當立即執行函數執行時,首先會把參數 i 的值復制一份,然后再創建函數作用域來執行函數,循環5次就會創建5個作用域,所以每個 li 元素訪問的都是不同作用域的 i 的值 。
3、立即執行函數和閉包的區別
setTimeout 依次輸出 0 1 2 3 4 5
for (var i = 0; i < 5; i++) { (function (i) { setTimeout(function () { console.log(i); }, 1000); })(i); } console.log(i);
第一個 5 很好輸出,因為for循環以后i就會變成5,剩下的我們可以使用立即執行函數來做。首先 JS中調用函數傳遞參數都是值傳遞 ,所以當立即執行函數執行時,首先會把參數 i 的值復制一份,然后再創建函數作用域來執行函數,循環5次就會創建5個作用域,所以1秒后幾乎會同時輸出 0 1 2 3 4 。
上面的現象也可以說是閉包,因為在外層的 function 里面還包含着 setTimeout 里面的 function 函數,而里面的 function 函數就訪問了外層 function 的 i 的值,由此就形成了一個閉包。每次循環時,將 i 的值保存在一個閉包中,當 setTimeout 中定義的操作執行時,就會訪問對應閉包保存的 i 值,所以輸出 0 1 2 3 4。
立即執行函數和閉包沒有什么關系,只是兩者會經常結合在一起使用而已,但兩者有本質的不同。
立即執行函數和閉包只是有一個共同優點就是能減少全局變量的使用。
立即執行函數只是函數的一種調用方式,只是聲明完之后立即執行,這類函數一般都只是調用一次,調用完之后會立即銷毀,不會占用內存。
閉包則主要是讓外部函數可以訪問內部函數的作用域,也減少了全局變量的使用,保證了內部變量的安全,但因被引用的內部變量不能被銷毀,增大了內存消耗,使用不當易造成內存泄露。
可參考:https://blog.csdn.net/Liu_yunzhao/article/details/90641956
