33-js-concepts之9. 高階函數(shù)

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》姊妹篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末夸盟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子像捶,更是在濱河造成了極大的恐慌,老刑警劉巖桩砰,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拓春,死亡現(xiàn)場離奇詭異,居然都是意外死亡亚隅,警方通過查閱死者的電腦和手機(jī)硼莽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煮纵,“玉大人懂鸵,你說我怎么就攤上這事⌒惺瑁” “怎么了匆光?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長酿联。 經(jīng)常有香客問我终息,道長夺巩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任周崭,我火速辦了婚禮柳譬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘续镇。我一直安慰自己美澳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布摸航。 她就那樣靜靜地躺著人柿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪忙厌。 梳的紋絲不亂的頭發(fā)上凫岖,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音逢净,去河邊找鬼哥放。 笑死,一個胖子當(dāng)著我的面吹牛爹土,可吹牛的內(nèi)容都是我干的甥雕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼胀茵,長吁一口氣:“原來是場噩夢啊……” “哼社露!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起琼娘,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤峭弟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脱拼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞒瘸,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年熄浓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了情臭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赌蔑,死狀恐怖俯在,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情娃惯,我是刑警寧澤跷乐,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站石景,受9級特大地震影響劈猿,放射性物質(zhì)發(fā)生泄漏拙吉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一揪荣、第九天 我趴在偏房一處隱蔽的房頂上張望筷黔。 院中可真熱鬧,春花似錦仗颈、人聲如沸佛舱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽请祖。三九已至,卻和暖如春脖祈,著一層夾襖步出監(jiān)牢的瞬間肆捕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工盖高, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慎陵,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓喻奥,卻偏偏與公主長得像席纽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子撞蚕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • The JavaScriptArrayobject is a global object thatis used ...
    skycolor閱讀 568評論 0 0
  • 創(chuàng)建數(shù)組 arr.length--- title: js正則表達(dá)式categories: javascriptda...
    angelwgh閱讀 1,391評論 0 2
  • 有目的的選擇是功利嗎润梯? 深圳已經(jīng)連續(xù)下雨一個月了,空氣粘膩到每個人都無形增加了幾分火氣甥厦,開了空調(diào)的小房間里纺铭,...
    塵間北閱讀 430評論 0 2
  • 圖層的幾個坐標(biāo)系 對于iOS來說,坐標(biāo)系的(0矫渔,0)點(diǎn)在左上角彤蔽,就是越往下,Y值越大庙洼。越往右,X值越大镊辕。 一個圖層...
    LeeMystique閱讀 16,684評論 1 53
  • 慈母手中線油够,游子身上衣。母親征懈,在大多數(shù)人的心中石咬,都是最溫暖柔軟的一部分。 我的母親卖哎,也不例外鬼悠。每次想起她删性,...
    笨笨姐妹閱讀 373評論 0 3