函數作爲返回值
高階函數除了可以接受函數作爲參數外,還可以把函數作爲結果值返回。例如:一個返回求和函數的函數
function lazy_sum(arr) {
var sum = function () {
return arr.reduce(function (x, y) {
return x + y;
});
}
return sum;
}
var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()
f(); // 15
閉包
當A函數的返回值是B函數時,A內部的局部變量還會被B函數引用,這裏面會出現一個問題:
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 16
f2(); // 16
f3(); // 16
全部都是16
!原因就在於返回的函數引用了變量i
,但它並非立刻執行。等到3個函數都返回時,它們所引用的變量i已經變成了4
,因此最終結果爲16
。
返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。
如果一定要引用循環變量,必須再創建一個函數,用該函數的參數綁定循環變量當前的值:
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 1
f2(); // 4
f3(); // 9
閉包的使用場景
閉包可以作爲一個攜帶狀態的函數,並且它的狀態可以完全對外隱藏起來:
'use strict';
function create_counter(initial) {
var x = initial || 0;
return {
inc: function () {
x += 1;
return x;
}
}
}
var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3
var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13
閉包還可以把多參數的函數變成單參數的函數。例如,要計算xy可以用Math.pow(x, y)
函數,不過考慮到經常計算x2或x3,我們可以利用閉包創建新的函數pow2
和pow3
:
function make_pow(n) {
return function (x) {
return Math.pow(x, n);
}
}
// 創建兩個新函數:
var pow2 = make_pow(2);
var pow3 = make_pow(3);
pow2(5); // 25
pow3(7); // 343