匿名函數:沒有名字的函數;
閉包:可訪問一個函數作用域裡的變量的函數;
一 匿名函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 普通函數
function box(){ // 函數名是box;
return 'Lee';
}
box(); // =>Lee; 調用函數;
// 匿名函數
function(){ // 匿名函數,會報錯;
return 'Lee';
}
// 通過表達式自我執行
(function(name){
console.log(name); // =>Lee;
})("Lee"); // "()"表示執行函數,並且可以傳參;
// 把匿名函數賦值給變量
var box = function(){ // 將匿名函數賦給變量;
return 'Lee';
};
console.log(box()); // 調用方式和函數調用相似;
// 函數裡的匿名函數
function box(){
return function(name){ // 函數裡的匿名函數,產生閉包;
return name;
};
};
console.log(box()("Lee")); // 函數box()調用匿名函數,並傳參;
二 閉包
閉包:有權訪問另一個函數作用域中的變量的函數;
創建閉包的常見方式:在一個函數內部創建另一個函數;通過另一個函數訪問這個函數的局部變量;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 通過閉包可以返回局部變量
function box(){
var user = 'Lee';
return function(){ // 通過匿名函數返回box()的局部變量user;
return user;
};
}
console.log(box()()); // =>Lee; 直接調用box()()來獲得匿名函數的返回值;
var b = box();
console.log(b()); // =>Lee; 另一種調用匿名函數方式;
// 優點:可以把局部變量駐留在內存中,可以避免使用全局變量;
// (全局變量污染導致應用程序不可預測性,每個模塊都可調用必將引來災難;所以推薦使用私有的,封裝的局部變量);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 缺點:
// 通過全局變量來累加
var age = 100; // 全局變量;
function box(){
age++; // 模塊級可以調用全局變量,進行累加;
};
box(); // 執行函數,累加一次;
console.log(age); // =>101; 輸出全局變量;
box(); // 執行函數,累加一次;
console.log(age); // =>102; 輸出全局變量;
// 通過局部變量無法實現累加
function box(){
var age = 100;
age++; // 實現累加;
return age;
}
console.log(box()); // =>101;
console.log(box()); // =>101; 無法實現累加,因為第二次調用函數時,函數內部變量age又被初始化了;
// 通過閉包可以實現局部變量的累加
function box(){
var age = 100;
return function(){ // 匿名函數內實現累加;
age++;
return age; // 並返回累加後的變量;
}; // 此時box()函數的局部變量age的值已經被修改為累加後的值;
}
var b = box(); // 給box()函數賦值給變量;
console.log(b()); // =>101; 調用匿名函數,累加一次;
console.log(b()); // =>102; 第二次調用匿名函數,累加兩次;
// PS:由於閉包裡作用域返回的局部變量資源不會被立刻銷毀回收,所以可能會占用更多的內存;所以過度使用閉包會導致性能下降;(將閉包引用在"私有作用域"中即可實現變量銷毀)
// 作用域鏈的機制導致一個問題,在循環中裡的匿名函數取得的任何變量都是最後一個值; ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 循環裡包含匿名函數
function box(){
var arr = [];
for(var i=0; i<5; i++){ // 當聲明變量i=5時,循環停止;而此時循環裡的變量i==5;
arr[i] = function(){ // arr[i]得到的只是沒有執行的匿名函數function(){};
return i;
};
};
return arr; // arr = [function,function,function,function,function];
}
var b = box(); // =>[function,function,function,function,function]; 得到函數box()返回的數組arr;
console.log(b.length); // =>5; 得到函數集合數組長度;
for(var i=0; i<b.length; i++){
console.log(box()[i]()); // =>5,5,5,5,5; 輸出每個函數的值,都是最後一個值;
}
// 上面的例子輸出的結果都是5,也就是循環後得到的最大i值;
// 因為b[i]調用的是匿名函數,匿名函數並沒有自我執行,等到調用的時候,box()已執行完畢,i早已變成5;
// 循環裡包含匿名函數-改1,自我執行匿名函數
function box(){
var arr = [];
for(var i=0; i<5; i++){
arr[i] = (function(num){ // arr[i]得到的是匿名函數執行後的結果數值0-4;
return num;
})(i); // 自我執行並傳參;
}
return arr;
}
var b = box(); // =>[0,1,2,3,4]; 此時b代表box()返回的數組;
for (var i = 0; i < b.length; i++) {
console.log(b[i]); // 0 1 2 3 4; 這裡返回的是數值;
};
// 例子中,我們讓匿名函數進行自我執行,導致最終返回給a[i]的是數組而不是函數了;最終導致b[0]-b[4]中保留了0,1,2,3,4的值;
// 循環裡包含匿名函數-改2,匿名函數裡再做個匿名函數;
function box(){
var arr = [];
for(var i=0; i<5; i++){
arr[i