JavaScript函數(shù)式編程究竟是什么?

摘要: 理解函數(shù)式編程兼搏。

Fundebug經(jīng)授權(quán)轉(zhuǎn)載症虑,版權(quán)歸原作者所有。

在長(zhǎng)時(shí)間學(xué)習(xí)和使用面向?qū)ο缶幊讨笥禄剩蹅兺艘徊絹?lái)考慮系統(tǒng)復(fù)雜性罩句。

在做了一些研究之后,我發(fā)現(xiàn)了函數(shù)式編程的概念敛摘,比如不變性和純函數(shù)门烂。這些概念使你能夠構(gòu)建無(wú)副作用的函數(shù),因此更容易維護(hù)具有其他優(yōu)點(diǎn)的系統(tǒng)兄淫。

在這篇文章中屯远,將通大量代碼示例來(lái)詳細(xì)介紹函數(shù)式編程和一些相關(guān)重要概念。

什么是函數(shù)式編程

函數(shù)式編程是一種編程范式捕虽,是一種構(gòu)建計(jì)算機(jī)程序結(jié)構(gòu)和元素的風(fēng)格氓润,它把計(jì)算看作是對(duì)數(shù)學(xué)函數(shù)的評(píng)估,避免了狀態(tài)的變化和數(shù)據(jù)的可變薯鳍。

純函數(shù)

當(dāng)我們想要理解函數(shù)式編程時(shí)咖气,需要知道的第一個(gè)基本概念是純函數(shù)挨措,但純函數(shù)又是什么鬼?

咱們?cè)趺粗酪粋€(gè)函數(shù)是否是純函數(shù)?這里有一個(gè)非常嚴(yán)格的定義:

  • 如果給定相同的參數(shù)崩溪,則返回相同的結(jié)果(也稱為確定性)浅役。
  • 它不會(huì)引起任何副作用。

如果給定相同的參數(shù)伶唯,則得到相同的結(jié)果

如果給出相同的參數(shù)觉既,它返回相同的結(jié)果。 想象一下乳幸,我們想要實(shí)現(xiàn)一個(gè)計(jì)算圓的面積的函數(shù)瞪讼。

不是純函數(shù)會(huì)這樣做榄审,接收radius 作為參數(shù)甫题,然后計(jì)算radius * radius * PI

    let PI = 3.14;
    
    const calculateArea = (radius) => radius * radius * PI;
    
    calculateArea(10); // returns 314.0

為什么這是一個(gè)不純函數(shù)?原因很簡(jiǎn)單,因?yàn)樗褂昧艘粋€(gè)沒(méi)有作為參數(shù)傳遞給函數(shù)的全局對(duì)象衙荐。

現(xiàn)在瓶埋,想象一些數(shù)學(xué)家認(rèn)為圓周率的值實(shí)際上是42并且修改了全局對(duì)象的值希柿。

不純函數(shù)得到10 * 10 * 42 = 4200。對(duì)于相同的參數(shù)(radius = 10)养筒,我們得到了不同的結(jié)果曾撤。

修復(fù)它:

    let PI = 3.14;
    
    const calculateArea = (radius, pi) => radius * radius * pi;
    
    calculateArea(10, PI); // returns 314.0

現(xiàn)在把 PI 的值作為參數(shù)傳遞給函數(shù),這樣就沒(méi)有外部對(duì)象引入晕粪。

  • 對(duì)于參數(shù)radius = 10PI = 3.14挤悉,始終都會(huì)得到相同的結(jié)果:314.0
  • 對(duì)于 radius = 10PI = 42巫湘,總是得到相同的結(jié)果:4200

讀取文件

下面函數(shù)讀取外部文件装悲,它不是純函數(shù),文件的內(nèi)容隨時(shí)可能都不一樣剩膘。

    const charactersCounter = (text) => `Character count: ${text.length}`;
    
    function analyzeFile(filename) {
      let fileContent = open(filename);
      return charactersCounter(fileContent);
    }

隨機(jī)數(shù)生成

任何依賴于隨機(jī)數(shù)生成器的函數(shù)都不能是純函數(shù)衅斩。

    function yearEndEvaluation() {
      if (Math.random() > 0.5) {
        return "You get a raise!";
      } else {
        return "Better luck next year!";
      }
    }

無(wú)明顯副作用

純函數(shù)不會(huì)引起任何可觀察到的副作用〉『郑可見(jiàn)副作用的例子包括修改全局對(duì)象或通過(guò)引用傳遞的參數(shù)畏梆。

現(xiàn)在,咱們要實(shí)現(xiàn)一個(gè)函數(shù)奈懒,該接收一個(gè)整數(shù)并返對(duì)該整數(shù)進(jìn)行加1操作且返回奠涌。

    let counter = 1;
    
    function increaseCounter(value) {
      counter = value + 1;
    }
    
    increaseCounter(counter);
    console.log(counter); // 2

該非純函數(shù)接收該值并重新分配counter,使其值增加1磷杏。

函數(shù)式編程不鼓勵(lì)可變性溜畅。我們修改全局對(duì)象,但是要怎么做才能讓它變得純函數(shù)呢极祸?只需返回增加1的值慈格。

    let counter = 1;
    
    const increaseCounter = (value) => value + 1;
    
    increaseCounter(counter); // 2
    console.log(counter); // 1

純函數(shù)increaseCounter返回2怠晴,但是counter值仍然是相同的。函數(shù)返回遞增的值浴捆,而不改變變量的值蒜田。

如果我們遵循這兩條簡(jiǎn)單的規(guī)則,就會(huì)更容易理解我們的程序⊙⌒海現(xiàn)在每個(gè)函數(shù)都是孤立的冲粤,不能影響系統(tǒng)的其他部分。

純函數(shù)是穩(wěn)定的页眯、一致的和可預(yù)測(cè)的梯捕。給定相同的參數(shù),純函數(shù)總是返回相同的結(jié)果窝撵。

咱們不需要考慮相同參數(shù)有不同結(jié)果的情況傀顾,因?yàn)樗肋h(yuǎn)不會(huì)發(fā)生。

純函數(shù)的好處

純函數(shù)代碼肯定更容易測(cè)試忿族,不需要 mock 任何東西,因此锣笨,我們可以使用不同的上下文對(duì)純函數(shù)進(jìn)行單元測(cè)試:

  • 給定一個(gè)參數(shù) A蝌矛,期望函數(shù)返回值 B
  • 給定一個(gè)參數(shù)C道批,期望函數(shù)返回值D

一個(gè)簡(jiǎn)單的例子是接收一組數(shù)字,并對(duì)每個(gè)數(shù)進(jìn)行加 1 這種沙雕的操作入撒。

    let list = [1, 2, 3, 4, 5];
    
    const incrementNumbers = (list) => list.map(number => number + 1);

接收numbers數(shù)組隆豹,使用map遞增每個(gè)數(shù)字,并返回一個(gè)新的遞增數(shù)字列表茅逮。

    incrementNumbers(list); // [2, 3, 4, 5, 6]

對(duì)于輸入[1,2,3,4,5]璃赡,預(yù)期輸出是[2,3,4,5,6]献雅。

不可變性

盡管時(shí)間變或者不變碉考,純函數(shù)大佬都是不變的。

當(dāng)數(shù)據(jù)是不可變的時(shí)挺身,它的狀態(tài)在創(chuàng)建后不能更改侯谁。

咱們不能更改不可變對(duì)象,如果非要來(lái)硬的章钾,剛需要深拷貝一個(gè)副本墙贱,然后操作這個(gè)副本。

在JS中贱傀,我們通常使用for循環(huán)惨撇,for的每次遍歷 i是個(gè)可變變量。

    var values = [1, 2, 3, 4, 5];
    var sumOfValues = 0;
    
    for (var i = 0; i < values.length; i++) {
      sumOfValues += values[i];
    }
    
    sumOfValues // 15

對(duì)于每次遍歷府寒,都在更改isumOfValue狀態(tài)魁衙,但是我們?nèi)绾卧诒闅v中處理可變性呢? 答案就是使用遞歸报腔。

    let list = [1, 2, 3, 4, 5];
    let accumulator = 0;
    
    function sum(list, accumulator) {
      if (list.length == 0) {
        return accumulator;
      }
    
      return sum(list.slice(1), accumulator + list[0]);
    }
    
    sum(list, accumulator); // 15
    list; // [1, 2, 3, 4, 5]
    accumulator; // 0

上面代碼有個(gè) sum 函數(shù),它接收一個(gè)數(shù)值向量剖淀。函數(shù)調(diào)用自身榄笙,直到 list為空退出遞歸。對(duì)于每次“遍歷”祷蝌,我們將把值添加到總accumulator中茅撞。

使用遞歸,咱們保持變量不變巨朦。不會(huì)更改listaccumulator變量米丘。它保持相同的值。

觀察:我們可以使用reduce來(lái)實(shí)現(xiàn)這個(gè)功能糊啡。這個(gè)在接下的高階函數(shù)內(nèi)容中討論拄查。

構(gòu)建對(duì)象的最終狀態(tài)也很常見(jiàn)。假設(shè)我們有一個(gè)字符串棚蓄,想把這個(gè)字符串轉(zhuǎn)換成url slug堕扶。

在Ruby的面向?qū)ο缶幊讨校蹅兛梢詣?chuàng)建一個(gè)類 UrlSlugify梭依,這個(gè)類有一個(gè)slugify方法來(lái)將字符串輸入轉(zhuǎn)換為url slug稍算。

    class UrlSlugify
      attr_reader :text
      
      def initialize(text)
        @text = text
      end
    
      def slugify!
        text.downcase!
        text.strip!
        text.gsub!(' ', '-')
      end
    end
    
    UrlSlugify.new(' I will be a url slug   ').slugify! # "i-will-be-a-url-slug"

上面使用的有命令式編程方式,首先用小寫(xiě)字母表示我們想在每個(gè)slugify進(jìn)程中做什么役拴,然后刪除無(wú)用的空格糊探,最后用連字符替換剩余的空格。

這種方式在整個(gè)過(guò)程中改變了輸入狀態(tài)河闰,顯然不符合純函數(shù)的概念科平。

這邊可以通過(guò)函數(shù)組合或函數(shù)鏈來(lái)來(lái)優(yōu)化。換句話說(shuō)姜性,函數(shù)的結(jié)果將用作下一個(gè)函數(shù)的輸入瞪慧,而不修改原始輸入字符串。

    const string = " I will be a url slug   ";
    
    const slugify = string =>
      string
        .toLowerCase()
        .trim()
        .split(" ")
        .join("-");
    
    slugify(string); // i-will-be-a-url-slug

上述代碼主要做了這幾件事:

  • toLowerCase:將字符串轉(zhuǎn)換為所有小寫(xiě)字母部念。
  • trim:刪除字符串兩端的空白弃酌。
  • splitjoin:用給定字符串中的替換替換所有匹配實(shí)例

代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道,事后為了解決這些BUG印机,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試矢腻,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug

引用透明性

接著實(shí)現(xiàn)一個(gè)square 函數(shù):

const square = (n) => n * n;

給定相同的輸入射赛,這個(gè)純函數(shù)總是有相同的輸出多柑。

    square(2); // 4
    square(2); // 4
    square(2); // 4
    // ...

2作為square函數(shù)的參數(shù)傳遞始終會(huì)返回4。這樣咱們可以把square(2)換成4楣责,我們的函數(shù)就是引用透明的竣灌。

基本上聂沙,如果一個(gè)函數(shù)對(duì)于相同的輸入始終產(chǎn)生相同的結(jié)果,那么它可以看作透明的初嘹。

有了這個(gè)概念及汉,咱們可以做的一件很酷的事情就是記住這個(gè)函數(shù)。假設(shè)有這樣的函數(shù)

    const sum = (a, b) => a + b;

用這些參數(shù)來(lái)調(diào)用它

    sum(3, sum(5, 8));

sum(5, 8) 總等于13屯烦,所以可以做些騷操作:

    sum(3, 13);

這個(gè)表達(dá)式總是得到16坷随,咱們可以用一個(gè)數(shù)值常數(shù)替換整個(gè)表達(dá)式,并把它記下來(lái)驻龟。

函數(shù)是 JS 中的一級(jí)公民

函數(shù)作為 JS 中的一級(jí)公民温眉,很風(fēng)騷,函數(shù)也可以被看作成值并用作數(shù)據(jù)使用翁狐。

  • 從常量和變量中引用它类溢。
  • 將其作為參數(shù)傳遞給其他函數(shù)。
  • 作為其他函數(shù)的結(jié)果返回它露懒。

其思想是將函數(shù)視為值闯冷,并將函數(shù)作為數(shù)據(jù)傳遞。通過(guò)這種方式懈词,我們可以組合不同的函數(shù)來(lái)創(chuàng)建具有新行為的新函數(shù)蛇耀。

假如我們有一個(gè)函數(shù),它對(duì)兩個(gè)值求和钦睡,然后將值加倍蒂窒,如下所示:

    const doubleSum = (a, b) => (a + b) * 2;

對(duì)應(yīng)兩個(gè)值求差躁倒,然后將值加倍:

    const doubleSubtraction = (a, b) => (a - b) * 2;

這些函數(shù)具有相似的邏輯荞怒,但區(qū)別在于運(yùn)算符的功能。 如果我們可以將函數(shù)視為值并將它們作為參數(shù)傳遞秧秉,我們可以構(gòu)建一個(gè)接收運(yùn)算符函數(shù)并在函數(shù)內(nèi)部使用它的函數(shù)褐桌。

    const sum = (a, b) => a + b;
    const subtraction = (a, b) => a - b;
    
    const doubleOperator = (f, a, b) => f(a, b) * 2;
    
    doubleOperator(sum, 3, 1); // 8
    doubleOperator(subtraction, 3, 1); // 4

f參數(shù)并用它來(lái)處理ab, 這里傳遞了sum函數(shù)和subtraction并使用doubleOperator函數(shù)進(jìn)行組合并創(chuàng)建新行為象迎。

高階函數(shù)

當(dāng)我們討論高階函數(shù)時(shí)荧嵌,通常包括以下幾點(diǎn):

  • 將一個(gè)或多個(gè)函數(shù)作為參數(shù)
  • 返回一個(gè)函數(shù)作為結(jié)果

上面實(shí)現(xiàn)的doubleOperator函數(shù)是一個(gè)高階函數(shù),因?yàn)樗鼘⒁粋€(gè)運(yùn)算符函數(shù)作為參數(shù)并使用它砾淌。

我們經(jīng)常用的filter啦撮、mapreduce都是高階函數(shù),Look see see汪厨。

Filter

對(duì)于給定的集合赃春,我們希望根據(jù)屬性進(jìn)行篩選。filter函數(shù)期望一個(gè)truefalse值來(lái)決定元素是否應(yīng)該包含在結(jié)果集合中劫乱。

如果回調(diào)表達(dá)式為真织中,過(guò)濾器函數(shù)將在結(jié)果集合中包含元素锥涕,否則,它不會(huì)狭吼。

一個(gè)簡(jiǎn)單的例子是层坠,當(dāng)我們有一個(gè)整數(shù)集合,我們只想要偶數(shù)刁笙。

命令式

使用命令式方式來(lái)獲取數(shù)組中所有的偶數(shù)破花,通常會(huì)這樣做:

  • 創(chuàng)建一個(gè)空數(shù)組evenNumbers
  • 遍歷數(shù)組 numbers
  • 將偶數(shù) push 到evenNumbers數(shù)組中
    var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    var evenNumbers = [];
    
    for (var i = 0; i < numbers.length; i++) {
      if (numbers[i] % 2 == 0) {
        evenNumbers.push(numbers[i]);
      }
    }
    
    console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10]

我們還可以使用filter高階函數(shù)來(lái)接收偶函數(shù)并返回一個(gè)偶數(shù)列表:

const even = n => n % 2 == 0;
const listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10]

我在 [Hacker Rank FP][2] 上解決的一個(gè)有趣問(wèn)題是[Filter Array問(wèn)題][3]。 問(wèn)題是過(guò)濾給定的整數(shù)數(shù)組疲吸,并僅輸出小于指定值X的那些值旧乞。

命令式做法通常是這樣的:

    var filterArray = function(x, coll) {
      var resultArray = [];
    
      for (var i = 0; i < coll.length; i++) {
        if (coll[i] < x) {
          resultArray.push(coll[i]);
        }
      }
    
      return resultArray;
    }
    
    console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0]

聲明式方式

對(duì)于上面的總是,我們更想要一種更聲明性的方法來(lái)解決這個(gè)問(wèn)題磅氨,如下所示:

    function smaller(number) {
      return number < this;
    }
    
    function filterArray(x, listOfNumbers) {
      return listOfNumbers.filter(smaller, x);
    }
    
    let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0];
    
    filterArray(3, numbers); // [2, 1, 0]

smaller的函數(shù)中使用 this,一開(kāi)始看起來(lái)有點(diǎn)奇怪尺栖,但是很容易理解。

filter函數(shù)中的第二個(gè)參數(shù)表示上面 this烦租, 也就是 x 值延赌。

我們也可以用map方法做到這一點(diǎn)。想象一下叉橱,有一組信息

    let people = [
      { name: "TK", age: 26 },
      { name: "Kaio", age: 10 },
      { name: "Kazumi", age: 30 }
    ]

我們希望過(guò)濾 age 大于 21 歲的人挫以,用 filter 方式

    const olderThan21 = person => person.age > 21;
    const overAge = people => people.filter(olderThan21);
    overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }]

map

map函數(shù)的主要思路是轉(zhuǎn)換集合。

map方法通過(guò)將函數(shù)應(yīng)用于其所有元素并根據(jù)返回的值構(gòu)建新集合來(lái)轉(zhuǎn)換集合窃祝。

假如我們不想過(guò)濾年齡大于 21 的人掐松,我們想做的是顯示類似這樣的:TK is 26 years old.

使用命令式,我們通常會(huì)這樣做:

    var people = [
      { name: "TK", age: 26 },
      { name: "Kaio", age: 10 },
      { name: "Kazumi", age: 30 }
    ];
    
    var peopleSentences = [];
    
    for (var i = 0; i < people.length; i++) {
      var sentence = people[i].name + " is " + people[i].age + " years old";
      peopleSentences.push(sentence);
    }
    
    console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']

聲明式會(huì)這樣做:

    const makeSentence = (person) => `${person.name} is ${person.age} years old`;
    
    const peopleSentences = (people) => people.map(makeSentence);
      
    peopleSentences(people);
    // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']

整個(gè)思想是將一個(gè)給定的數(shù)組轉(zhuǎn)換成一個(gè)新的數(shù)組粪小。

另一個(gè)有趣的HackerRank問(wèn)題是[更新列表問(wèn)題][3]大磺。我們想要用一個(gè)數(shù)組的絕對(duì)值來(lái)更新它的值。

例如探膊,輸入[1,2,3杠愧,- 4,5]需要輸出為[1,2,3,4,5]-4的絕對(duì)值是4逞壁。

一個(gè)簡(jiǎn)單的解決方案是每個(gè)集合中值的就地更新流济,很危險(xiǎn)的作法

    var values = [1, 2, 3, -4, 5];
    
    for (var i = 0; i < values.length; i++) {
      values[i] = Math.abs(values[i]);
    }
    
    console.log(values); // [1, 2, 3, 4, 5]

我們使用Math.abs函數(shù)將值轉(zhuǎn)換為其絕對(duì)值并進(jìn)行就地更新。

這種方式不是最做解腌闯。

首先绳瘟,前端我們學(xué)習(xí)了不變性,知道不可變性讓函數(shù)更加一致和可預(yù)測(cè)姿骏,咱們的想法是建立一個(gè)具有所有絕對(duì)值的新集合糖声。

其次,為什么不在這里使用map來(lái)“轉(zhuǎn)換”所有數(shù)據(jù)

我的第一個(gè)想法是測(cè)試Math.abs函數(shù)只處理一個(gè)值。

    Math.abs(-1); // 1
    Math.abs(1); // 1
    Math.abs(-2); // 2
    Math.abs(2); // 2

我們想把每個(gè)值轉(zhuǎn)換成一個(gè)正值(絕對(duì)值)姨丈。

現(xiàn)在知道如何對(duì)一個(gè)值執(zhí)行絕對(duì)值操作畅卓,可以使用此函數(shù)作為參數(shù)傳遞給map函數(shù)。

還記得高階函數(shù)可以接收函數(shù)作為參數(shù)并使用它嗎? 是的蟋恬,map函數(shù)可以做到這一點(diǎn)

    let values = [1, 2, 3, -4, 5];
    
    const updateListMap = (values) => values.map(Math.abs);
    
    updateListMap(values); // [1, 2, 3, 4, 5]

Reduce

reduce函數(shù)的思想是接收一個(gè)函數(shù)和一個(gè)集合翁潘,并返回通過(guò)組合這些項(xiàng)創(chuàng)建的值。

常見(jiàn)的的一個(gè)例子是獲取訂單的總金額歼争。

假設(shè)你在一個(gè)購(gòu)物網(wǎng)站拜马,已經(jīng)將產(chǎn)品1、產(chǎn)品2沐绒、產(chǎn)品3和產(chǎn)品4添加到購(gòu)物車(訂單)中×┟В現(xiàn)在,我們要計(jì)算購(gòu)物車的總數(shù)量:

以命令式的方式乔遮,就是便利訂單列表并將每個(gè)產(chǎn)品金額與總金額相加扮超。

    var orders = [
      { productTitle: "Product 1", amount: 10 },
      { productTitle: "Product 2", amount: 30 },
      { productTitle: "Product 3", amount: 20 },
      { productTitle: "Product 4", amount: 60 }
    ];
    
    var totalAmount = 0;
    
    for (var i = 0; i < orders.length; i++) {
      totalAmount += orders[i].amount;
    }
    
    console.log(totalAmount); // 120

使用reduce,我們可以構(gòu)建一個(gè)函數(shù)來(lái)處理量計(jì)算sum并將其作為參數(shù)傳遞給reduce函數(shù)蹋肮。

    let shoppingCart = [
      { productTitle: "Product 1", amount: 10 },
      { productTitle: "Product 2", amount: 30 },
      { productTitle: "Product 3", amount: 20 },
      { productTitle: "Product 4", amount: 60 }
    ];
    
    const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount;
    
    const getTotalAmount = (shoppingCart) => shoppingCart.reduce(sumAmount, 0);
    
    getTotalAmount(shoppingCart); // 120

這里有shoppingCart出刷,接收當(dāng)前currentTotalAmount的函數(shù)sumAmount,以及對(duì)它們求和的order對(duì)象坯辩。

咱們也可以使用mapshoppingCart轉(zhuǎn)換為一個(gè)amount集合馁龟,然后使用reduce函數(shù)和sumAmount函數(shù)。

    const getAmount = (order) => order.amount;
    const sumAmount = (acc, amount) => acc + amount;

    function getTotalAmount(shoppingCart) {
      return shoppingCart
        .map(getAmount)
        .reduce(sumAmount, 0);
    }
    
    getTotalAmount(shoppingCart); // 120

getAmount接收product對(duì)象并只返回amount值漆魔,即[10,30,20,60]坷檩,然后,reduce通過(guò)相加將所有項(xiàng)組合起來(lái)改抡。

三個(gè)函數(shù)的示例

看了每個(gè)高階函數(shù)的工作原理矢炼。這里為你展示一個(gè)示例,說(shuō)明如何在一個(gè)簡(jiǎn)單的示例中組合這三個(gè)函數(shù)雀摘。

說(shuō)到購(gòu)物車裸删,假設(shè)我們的訂單中有這個(gè)產(chǎn)品列表

    let shoppingCart = [
      { productTitle: "Functional Programming", type: "books", amount: 10 },
      { productTitle: "Kindle", type: "eletronics", amount: 30 },
      { productTitle: "Shoes", type: "fashion", amount: 20 },
      { productTitle: "Clean Code", type: "books", amount: 60 }
    ]

假如相要想要購(gòu)物車?yán)镱愋蜑?books的總數(shù),通常會(huì)這樣做:

  • 過(guò)濾 type 為 books的
  • 使用map將購(gòu)物車轉(zhuǎn)換為amount集合阵赠。
  • reduce將所有項(xiàng)加起來(lái)。
    let shoppingCart = [
      { productTitle: "Functional Programming", type: "books", amount: 10 },
      { productTitle: "Kindle", type: "eletronics", amount: 30 },
      { productTitle: "Shoes", type: "fashion", amount: 20 },
      { productTitle: "Clean Code", type: "books", amount: 60 }
    ]
    
    const byBooks = (order) => order.type == "books";
    const getAmount = (order) => order.amount;
    const sumAmount = (acc, amount) => acc + amount;
    
    function getTotalAmount(shoppingCart) {
      return shoppingCart
        .filter(byBooks)
        .map(getAmount)
        .reduce(sumAmount, 0);
    }
    
    getTotalAmount(shoppingCart); // 70

代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道肌稻,事后為了解決這些BUG清蚀,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug爹谭。

關(guān)于Fundebug

Fundebug專注于JavaScript枷邪、微信小程序、微信小游戲诺凡、支付寶小程序东揣、React Native践惑、Node.js和Java線上應(yīng)用實(shí)時(shí)BUG監(jiān)控。 自從2016年雙十一正式上線嘶卧,F(xiàn)undebug累計(jì)處理了20億+錯(cuò)誤事件尔觉,付費(fèi)客戶有陽(yáng)光保險(xiǎn)、核桃編程芥吟、荔枝FM侦铜、掌門(mén)1對(duì)1、微脈钟鸵、青團(tuán)社等眾多品牌企業(yè)钉稍。歡迎大家免費(fèi)試用!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棺耍,一起剝皮案震驚了整個(gè)濱河市贡未,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒙袍,老刑警劉巖羞秤,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異左敌,居然都是意外死亡瘾蛋,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)矫限,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哺哼,“玉大人,你說(shuō)我怎么就攤上這事叼风∪《” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵无宿,是天一觀的道長(zhǎng)茵汰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)孽鸡,這世上最難降的妖魔是什么蹂午? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮彬碱,結(jié)果婚禮上豆胸,老公的妹妹穿的比我還像新娘。我一直安慰自己巷疼,他們只是感情好晚胡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般估盘。 火紅的嫁衣襯著肌膚如雪瓷患。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天遣妥,我揣著相機(jī)與錄音擅编,去河邊找鬼。 笑死燥透,一個(gè)胖子當(dāng)著我的面吹牛沙咏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播班套,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肢藐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了吱韭?” 一聲冷哼從身側(cè)響起吆豹,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎理盆,沒(méi)想到半個(gè)月后痘煤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猿规,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年衷快,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姨俩。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蘸拔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出环葵,到底是詐尸還是另有隱情调窍,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布张遭,位于F島的核電站邓萨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏菊卷。R本人自食惡果不足惜缔恳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望的烁。 院中可真熱鬧褐耳,春花似錦、人聲如沸渴庆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)襟雷。三九已至刃滓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耸弄,已是汗流浹背咧虎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留计呈,地道東北人砰诵。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像捌显,于是被迫代替她去往敵國(guó)和親茁彭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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