JavaScript - 函數(shù)式編程

JavaScript語言從一誕生,就具有函數(shù)式編程的烙印。它將函數(shù)作為一種獨立的數(shù)據(jù)類型篙悯,與其他數(shù)據(jù)類型處于完全平等的地位单匣。在JavaScript語言中夕凝,你可以采用面向?qū)ο缶幊蹋部梢圆捎煤瘮?shù)式編程户秤。有人甚至說码秉,JavaScript是有史以來第一種被大規(guī)模采用的函數(shù)式編程語言。

ES6的種種新增功能鸡号,使得函數(shù)式編程變得更方便转砖、更強大。本章介紹ES6如何進行函數(shù)式編程鲸伴。

柯里化

柯里化(currying)指的是將一個多參數(shù)的函數(shù)拆分成一系列函數(shù)府蔗,每個拆分后的函數(shù)都只接受一個參數(shù)(unary)。

function add (a, b) {
  return a + b;
}

add(1, 1) // 2

上面代碼中汞窗,函數(shù)add接受兩個參數(shù)ab姓赤。

柯里化就是將上面的函數(shù)拆分成兩個函數(shù),每個函數(shù)都只接受一個參數(shù)仲吏。

function add (a) {
  return function (b) {
    return a + b;
  }
}
// 或者采用箭頭函數(shù)寫法
const add = x => y => x + y;

const f = add(1);
f(1) // 2

上面代碼中不铆,函數(shù)add只接受一個參數(shù)a蝌焚,返回一個函數(shù)f。函數(shù)f也只接受一個參數(shù)b誓斥。

函數(shù)合成

函數(shù)合成(function composition)指的是只洒,將多個函數(shù)合成一個函數(shù)。

const compose = f => g => x => f(g(x));

const f = compose (x => x * 4) (x => x + 3);
f(2) // 20

上面代碼中劳坑,compose就是一個函數(shù)合成器毕谴,用于將兩個函數(shù)合成一個函數(shù)。

可以發(fā)現(xiàn)泡垃,柯里化與函數(shù)合成有著密切的聯(lián)系析珊。前者用于將一個函數(shù)拆成多個函數(shù),后者用于將多個函數(shù)合并成一個函數(shù)蔑穴。

參數(shù)倒置

參數(shù)倒置(flip)指的是改變函數(shù)前兩個參數(shù)的順序忠寻。

var divide = (a, b) => a / b;
var flip = f.flip(divide);

flip(10, 5) // 0.5
flip(1, 10) // 10

var three = (a, b, c) => [a, b, c];
var flip = f.flip(three);
flip(1, 2, 3); // => [2, 1, 3]

上面代碼中,如果按照正常的參數(shù)順序存和,10除以5等于2奕剃。但是,參數(shù)倒置以后得到的新函數(shù)捐腿,結(jié)果就是5除以10纵朋,結(jié)果得到0.5。如果原函數(shù)有3個參數(shù)茄袖,則只顛倒前兩個參數(shù)的位置操软。

參數(shù)倒置的代碼非常簡單。

let f = {};
f.flip =
  fn =>
    (a, b, ...args) => fn(b, a, ...args.reverse());

執(zhí)行邊界

執(zhí)行邊界(until)指的是函數(shù)執(zhí)行到滿足條件為止宪祥。

let condition = x => x > 100;
let inc = x => x + 1;
let until = f.until(condition, inc);

until(0) // 101

condition = x => x === 5;
until = f.until(condition, inc);

until(3) // 5

上面代碼中聂薪,第一段的條件是執(zhí)行到x大于100為止,所以x初值為0時蝗羊,會一直執(zhí)行到101藏澳。第二段的條件是執(zhí)行到等于5為止,所以x最后的值是5耀找。

執(zhí)行邊界的實現(xiàn)如下翔悠。

let f = {};
f.until = (condition, f) =>
  (...args) => {
    var r = f.apply(null, args);
    return condition(r) ? r : f.until(condition, f)(r);
  };

上面代碼的關(guān)鍵就是,如果滿足條件就返回結(jié)果野芒,否則不斷遞歸執(zhí)行蓄愁。

隊列操作

隊列(list)操作包括以下幾種。

  • head: 取出隊列的第一個非空成員狞悲。
  • last: 取出有限隊列的最后一個非空成員涝登。
  • tail: 取出除了“隊列頭”以外的其他非空成員。
  • init: 取出除了“隊列尾”以外的其他非空成員效诅。

下面是例子胀滚。

f.head(5, 27, 3, 1) // 5
f.last(5, 27, 3, 1) // 1
f.tail(5, 27, 3, 1) // [27, 3, 1]
f.init(5, 27, 3, 1) // [5, 27, 3]

這些方法的實現(xiàn)如下。

let f = {};
f.head = (...xs) => xs[0];
f.last = (...xs) => xs.slice(-1);
f.tail = (...xs) => Array.prototype.slice.call(xs, 1);
f.init = (...xs) => xs.slice(0, -1);

合并操作

合并操作分為concatconcatMap兩種乱投。前者就是將多個數(shù)組合成一個咽笼,后者則是先處理一下參數(shù),然后再將處理結(jié)果合成一個數(shù)組戚炫。

f.concat([5], [27], [3]) // [5, 27, 3]
f.concatMap(x => 'hi ' + x, 1, [[2]], 3) // ['hi 1', 'hi 2', 'hi 3']

這兩種方法的實現(xiàn)代碼如下剑刑。

let f = {};
f.concat =
  (...xs) => xs.reduce((a, b) => a.concat(b));
f.concatMap =
  (f, ...xs) => f.concat(xs.map(f));

配對操作

配對操作分為zipzipWith兩種方法。zip操作將兩個隊列的成員双肤,一一配對施掏,合成一個新的隊列。如果兩個隊列不等長茅糜,較長的那個隊列多出來的成員七芭,會被忽略。zipWith操作的第一個參數(shù)是一個函數(shù)蔑赘,然后會將后面的隊列成員一一配對狸驳,輸入該函數(shù),返回值就組成一個新的隊列缩赛。

下面是例子耙箍。

let a = [0, 1, 2];
let b = [3, 4, 5];
let c = [6, 7, 8];

f.zip(a, b) // [[0, 3], [1, 4], [2, 5]]
f.zipWith((a, b) => a + b, a, b, c) // [9, 12, 15]

上面代碼中,zipWith方法的第一個參數(shù)是一個求和函數(shù)酥馍,它將后面三個隊列的成員辩昆,一一配對進行相加。

這兩個方法的實現(xiàn)如下旨袒。

let f = {};

f.zip = (...xs) => {
  let r = [];
  let nple = [];
  let length = Math.min.apply(null, xs.map(x => x.length));

  for (var i = 0; i < length; i++) {
    xs.forEach(
      x => nple.push(x[i])
    );

    r.push(nple);
    nple = [];
  }

  return r;
};

f.zipWith = (op, ...xs) =>
  f.zip.apply(null, xs).map(
    (x) => x.reduce(op)
  );

參考鏈接

函數(shù)式編程--轉(zhuǎn)自:阮一峰《ECMAScript 6 入門》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汁针,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子峦失,更是在濱河造成了極大的恐慌扇丛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尉辑,死亡現(xiàn)場離奇詭異帆精,居然都是意外死亡,警方通過查閱死者的電腦和手機隧魄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門卓练,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人购啄,你說我怎么就攤上這事襟企。” “怎么了狮含?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵顽悼,是天一觀的道長曼振。 經(jīng)常有香客問我,道長蔚龙,這世上最難降的妖魔是什么冰评? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮木羹,結(jié)果婚禮上甲雅,老公的妹妹穿的比我還像新娘。我一直安慰自己坑填,他們只是感情好抛人,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布越驻。 她就那樣靜靜地躺著年鸳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耍目。 梳的紋絲不亂的頭發(fā)上蚪黑,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天盅惜,我揣著相機與錄音,去河邊找鬼忌穿。 笑死抒寂,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的掠剑。 我是一名探鬼主播屈芜,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼朴译!你這毒婦竟也來了井佑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤眠寿,失蹤者是張志新(化名)和其女友劉穎躬翁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盯拱,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡盒发,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狡逢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宁舰。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖奢浑,靈堂內(nèi)的尸體忽然破棺而出蛮艰,到底是詐尸還是另有隱情,我是刑警寧澤雀彼,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布壤蚜,位于F島的核電站即寡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏仍律。R本人自食惡果不足惜嘿悬,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望水泉。 院中可真熱鬧,春花似錦窒盐、人聲如沸草则。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炕横。三九已至,卻和暖如春葡粒,著一層夾襖步出監(jiān)牢的瞬間份殿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工嗽交, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卿嘲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓夫壁,卻偏偏與公主長得像拾枣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盒让,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

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