函式宣告
函式宣告時必須有函式名
function fn(){};
複製程式碼
函式表示式
函式表示式中的函式可以為匿名函式,也可以有函式名,但是該函式不能直接使用,只能通過表示式左邊的變數 fn 來呼叫
var fn = function(){};
複製程式碼
看看兩者區別
function a(){
console.log("函式宣告");
}
var b = function(){
console.log("函式表示式");
}
a(); //函式申明
b(); //函式表示式
複製程式碼
a(); //函式宣告
b(); //報錯
function a(){
console.log("函式宣告");
}
var b = function(){
console.log("函式表示式");
}
複製程式碼
為什麼會有這樣的結果?
原因: function a(){} 為函式宣告,程式執行前就已經存在;var b = function(){} 為函式表示式,屬於按順序執行,所以 b() 會報錯
進入IIFE (立即執行的函式表示式)
在ES5中,是沒有塊級作用域的概念的;我們主要通過匿名函式的方式來塊級作用域。
用作塊級作用域(私有作用域)的匿名函式的語法:
(function() { //此處是塊級(私有)作用域 })();
!function () { //此處是塊級(私有)作用域 } ();
~function () { //此處是塊級(私有)作用域 } ();
-function () { //此處是塊級(私有)作用域 } ();
+function () { //此處是塊級(私有)作用域 } ();
//這些都是立即執行的函式表示式的寫法
//定義並立即呼叫了一個匿名函式。將函式宣告包含在一對圓括號中,表示它實際上是一個函式表示式。
複製程式碼
IIFE 寫法的產生:
var a = function() { console.log("IIFE 寫法的產生"); };
a(); //IIFE 寫法的產生
//我們將一個匿名函式賦值給了一個全域性變數a,然後呼叫了這個函式
複製程式碼
衍生出 IIFE 寫法
(function(){
console.log("這是一個立即執行的函式");
})();
//第一個圓括號:將匿名函式轉換為函式表示式
//第二個圓括號:立即執行匿名函式(當然,你也可以設定一個函式名)
複製程式碼
總結: 1. 建立塊級(私有)作用域,避免了向全域性作用域中新增變數和函式,因此也避免了多人開發中全域性變數和函式的命名衝突
2.IIFE中定義的任何變數和函式,都會在執行結束時被銷燬。這種做法可以減少閉包占用的記憶體問題,因為沒有指向匿名函式的引用。只要函式執行完畢,就可以立即銷燬其作用域鏈
常用例項
預期: 使用 setTimeout 迴圈輸出 0 1 2 3 4 5
for(var i = 0; i <= 5; i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);
}
//結果:1秒內輸出6個6
複製程式碼
原因: 超時的回撥函式都將在迴圈完成之後立即執行。
解決方法:
方法一:
for(var i = 0; i <= 5; i++){
(function(){
var j = i;
setTimeout(function timer(){
console.log(j);
}, j*1000)
})();
}
//結果: 0 1 2 3 4 5
複製程式碼
方法二:
for(var i = 0; i <= 5; i++){
(function(j){
setTimeout(function timer(){
console.log(j);
}, j*1000)
})(i);
}
//結果: 0 1 2 3 4 5
複製程式碼
IIFE 為每次迭代建立了新的作用域,這給了超時回撥函式一個機會在每次迭代時閉包一個新的作用域。