1.概念
一個函數(shù)接受另一個函數(shù)作為參數(shù),這就是高階函數(shù)行拢。
e.g.
function add(x, y, f) {
return f(x) + f(y);
}
add(-5, 6, Math.abs); // 11
2. ES5中新增的Array方法
1) forEach
[1, 2, 3, 4].forEach(console.log);
// 1 0 [1, 2, 3, 4]
// 2 1 [1, 2, 3, 4]
// 3 2 [1, 2, 3, 4]
// 4 3 [1, 2, 3, 4]
由此可見,forEach參數(shù)中的回調(diào)函數(shù)接收三個參數(shù)荠耽,分別是value, index, array眶根,即元素值、元素索引泛啸、數(shù)組本身绿语。對此,我們有:
( 注意index在第二個參數(shù)位置)
var sum = 0;
[1, 2, 3, 4].forEach(function (item, index, array) {
console.log(array[index] == item); // true
sum += item;
});
alert(sum); // 10
forEach除了接收一個回調(diào)函數(shù),還可以接收一個上下文參數(shù)(可選吕粹,默認(rèn)是全局對象window)伍纫,即:
array.forEach(callback,[ thisObject])
var database = {
users: ["張含韻", "江一燕", "李小璐"],
sendEmail: function (user) {
if (this.isValidUser(user)) {
console.log("你好," + user);
} else {
console.log("抱歉昂芜,"+ user +"莹规,你不是本家人");
}
},
isValidUser: function (user) {
return /^張/.test(user);
}
};
// 給每個人發(fā)郵件
database.users.forEach( // database.users中人遍歷
database.sendEmail, // 發(fā)送郵件
database // 使用database代替上面標(biāo)紅的this
);
// 結(jié)果:
// 你好,張含韻
// 抱歉泌神,江一燕良漱,你不是本家人
// 抱歉,李小璐欢际,你不是本家人
2) map
即“映射”母市,callback要有返回值,否則會返回一個全是undefined的數(shù)組损趋。
array.map(callback,[ thisObject]);
可以利用map獲得對象數(shù)組中的特定屬性值:
var users = [
{name: "張含韻", "email": "zhang@email.com"},
{name: "江一燕", "email": "jiang@email.com"},
{name: "李小璐", "email": "li@email.com"}
];
var emails = users.map(function (user) { return user.email; });
console.log(emails.join(", ")); // zhang@email.com, jiang@email.com, li@email.com
3) filter
返回過濾后的新數(shù)組患久。callback函數(shù)需要返回值,弱等于Boolean值浑槽。
var data = [0, 1, 2, 3];
var arrayFilter = data.filter(function(item) {
return item;
});
console.log(arrayFilter); // [1, 2, 3]
var emailsZhang = users
// 獲得郵件
.map(function (user) { return user.email; })
// 篩選出zhang開頭的郵件
.filter(function(email) { return /^zhang/.test(email); });
console.log(emailsZhang.join(", ")); // zhang@email.com
4) some
數(shù)組中的某些項(xiàng)是否符合條件(至少1個)蒋失,只要發(fā)現(xiàn)一個符合立刻返回true。返回值為true或false桐玻。
var scores = [5, 8, 3, 10];
var current = 7;
function higherThanCurrent(score) {
return score > current;
}
if (scores.some(higherThanCurrent)) {
alert("朕準(zhǔn)了篙挽!");
}
// 朕準(zhǔn)了!
5) every
和some相反镊靴,要全部符合條件才返回true铣卡,否則返回false。
if (scores.every(higherThanCurrent)) {
console.log("朕準(zhǔn)了偏竟!");
} else {
console.log("來人煮落,拖出去斬了!");
}
// 來人踊谋,拖出去斬了蝉仇!
6) indexOf
略
7) lastIndexOf
略
8) reduce
callback函數(shù)接受4個參數(shù):之前值、當(dāng)前值褪子、索引值以及數(shù)組本身量淌。initialValue參數(shù)可選,表示初始值嫌褪。若指定呀枢,則當(dāng)作最初使用的previous值;如果缺省笼痛,則使用數(shù)組的第一個元素作為previous初始值裙秋,同時(shí)current往后排一位琅拌,相比有initialValue值少一次迭代。
array.reduce(callback[, initialValue])
var sum = [1, 2, 3, 4].reduce(function (previous, current, index, array) {
return previous + current;
});
console.log(sum); // 10
說明:
因?yàn)閕nitialValue不存在摘刑,因此一開始的previous值等于數(shù)組的第一個元素进宝。
從而current值在第一次調(diào)用的時(shí)候就是2.
最后兩個參數(shù)為索引值index以及數(shù)組本身array。
// 初始設(shè)置
previous = initialValue = 1, current = 2
// 第一次迭代
previous = (1 + 2) = 3, current = 3
// 第二次迭代
previous = (3 + 3) = 6, current = 4
// 第三次迭代
previous = (6 + 4) = 10, current = undefined (退出)
二維數(shù)組扁平化:
var matrix = [
[1, 2],
[3, 4],
[5, 6]
];
// 二維數(shù)組扁平化
var flatten = matrix.reduce(function (previous, current) {
return previous.concat(current);
});
console.log(flatten); // [1, 2, 3, 4, 5, 6]
類似的reduceRight枷恕,從數(shù)組末尾開始:
var data = [1, 2, 3, 4];
var specialDiff = data.reduceRight(function (previous, current, index) {
if (index == 0) {
return previous + current;
}
return previous - current;
});
console.log(specialDiff); // 0
// 初始設(shè)置
index = 3, previous = initialValue = 4, current = 3
// 第一次迭代
index = 2, previous = (4- 3) = 1, current = 2
// 第二次迭代
index = 1, previous = (1 - 2) = -1, current = 1
// 第三次迭代
index = 0, previous = (-1 + 1) = 0, current = undefined (退出)
3.Reducer深入
首先是基本函數(shù):
var words = [ "You", "have", "written", "something", "very", "interesting" ];
function strUppercase(str) { return str.toUpperCase(); }
function isLongEnough(str) {
return str.length >= 5;
}
function isShortEnough(str) {
return str.length <= 10;
}
要對數(shù)組過濾党晋,可以直接map、連續(xù)filter徐块,但顯得過于重復(fù)未玻,且每個 filter(..) 方法都會產(chǎn)生一個單獨(dú)的 observable 值,數(shù)據(jù)量大時(shí)就會出現(xiàn)性能問題胡控。所以可以使用reduce扳剿,initialValue設(shè)為空數(shù)組[]作為初始的list:
function strUppercaseReducer(list,str) {
return list.concat( [strUppercase( str )] );
}
function isLongEnoughReducer(list,str) {
if (isLongEnough( str )) return list.concat( [str] );
return list;
}
function isShortEnoughReducer(list,str) {
if (isShortEnough( str )) return list.concat( [str] );
return list;
}
除了判斷函數(shù)不同其他基本相同,所以可以利用閉包把判斷函數(shù)作為參數(shù)昼激,建立一個filterReducer庇绽,同理還有mapReducer:
function filterReducer(predicateFn) {
return function reducer(list, val) {
if (predicateFn(val)) {
return list.concat( [val] );
}
return list;
}
}
var isLongEnoughReducer = filterReducer( isLongEnough );
var isShortEnoughReducer = filterReducer( isShortEnough );
function mapReducer(fn) {
return function reducer(list, val) {
return list.concat( [fn(val)] );
}
}
var strToUppercaseReducer = mapReducer( strUppercase );
調(diào)用鏈就可以寫成這樣:
words
.reduce( strUppercaseReducer, [] )
.reduce( isLongEnoughReducer, [] )
.reduce( isShortEnough, [] )
.reduce( strConcat, "" );
// "WRITTENSOMETHING"
提取共用組合邏輯
仔細(xì)觀察上面的 mapReducer(..) 和 filterReducer(..) 函數(shù)。你發(fā)現(xiàn)共享功能了嗎橙困?
這部分:
return list.concat( .. );
// 或者
return list;
所以還可以再提取出一個listCombination函數(shù)出來:
function listCombination(list,val) { return list.concat( [ val ] ); }
所以最新的filterReducer和mapReducer如下:
function filterReducer(predicateFn) {
return function reducer(list, val) {
if (predicateFn(val)) {
return listCombination( list, val );
}
return list;
}
}
function mapReducer(fn) {
return function reducer(list, val) {
return listCombination( list, fn(val) );
}
}
進(jìn)一步瞧掺,可以使用不同的類似listCombination的工具函數(shù),所以再對reducer進(jìn)行改造如下:
function filterReducer(predicateFn,combinationFn) {
return function reducer(list,val){
if (predicateFn( val )) return combinationFn( list, val );
return list;
};
}
function mapReducer(mapperFn,combinationFn) {
return function reducer(list,val){
return combinationFn( list, mapperFn( val ) );
};
}
var strToUppercaseReducer = mapReducer( strUppercase, listCombination );
var isLongEnoughReducer = filterReducer( isLongEnough, listCombination );
var isShortEnoughReducer = filterReducer( isShortEnough, listCombination );
將這些實(shí)用函數(shù)定義為接收兩個參數(shù)而不是一個參數(shù)不太方便組合纷宇,因此我們使用我們的 curry(..) (柯里化)方法:
var curriedMapReducer = curry( function mapReducer(mapperFn,combinationFn){
return function reducer(list,val){
return combinationFn( list, mapperFn( val ) );
};
} );
var curriedFilterReducer = curry( function filterReducer(predicateFn,combinationFn){
return function reducer(list,val){
if (predicateFn( val )) return combinationFn( list, val );
return list;
};
} );
var strToUppercaseReducer =
curriedMapReducer( strUppercase )( listCombination );
var isLongEnoughReducer =
curriedFilterReducer( isLongEnough )( listCombination );
var isShortEnoughReducer =
curriedFilterReducer( isShortEnough )( listCombination );
參考資料:
ES5中新增的Array方法詳細(xì)說明
翻譯連載 | 附錄 A:Transducing(上)-《JavaScript輕量級函數(shù)式編程》 |《你不知道的JS》姊妹篇