摘要:源起函數式編程近幾年非常流行經常可以在網上看到別人討論相關話題我機緣巧合之下在上看到有人提到一個講函數式編程的視頻看過之后突然茅塞頓開瞬間把之前零碎的關于函數式編程的知識一下子都聯系了起來于是自己希望趁有空把這些知識總結一下這樣既可以回顧下
源起
函數式編程近幾年非常流行,經常可以在網上看到別人討論相關話題. 我機緣巧合之下在github上看到有人提到一個講js函數式編程的視頻,看過之后突然茅塞頓開,瞬間把之前零碎的關于函數式編程的知識一下子都聯系了起來, 于是自己希望趁有空把這些知識總結一下, 這樣既可以回顧下知識耶沒準能幫到一些對函數式編程感興趣的朋友們.
為什么需要函數式編程其實千萬別被這看似高深的名字嚇怕了, 其實我們碼農用這種編程思想無非是為了能更爽的寫好代碼. 總結一下函數式編程的優點有以下兩個優點:
Less bug: 可讀性高, 邏輯上更容易理解.
less Time: 更高的抽象層級, 代碼高度可復用.
高階函數(Higher-order functions)這里又一個炫酷屌的名字: "高階函數". 別怕, 本文并不涉及高等數學的知識就可以讓你了解高階函數的概念. 但是等等! 高階函數跟函數式編程又有毛關系? 其實簡單的說就是高階函數的特性讓我們可以實現函數式編程.
廢話不多說, 下面就簡單說下什么是高階函數:
Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions.
從定義可以知道高階函數就是可以把函數作為變量傳入別的函數, 或者一個函數的返回值是一個函數的函數. 如果上面的話把你繞暈了那么我來簡單的概括下就是函數可以被當作變量在程序中傳來傳去. 下面舉幾個例子:
//把函數當成變量 var foo = (x) => console.log(x) //把函數當作參數傳入另一個函數 var bar = (y, fn) => fn(y) foo("FP is good") // FP is good bar("FP is great", foo) //FP is great
下面這個例子更能體現高階函數的便利性, 如果我們要遍歷數組并且打印數組元素的信息, 你會怎么做?
var arr = [1,1,2,3,5,8] function traverseArray(arr) { for (let i = 0; i < arr.length; i++) { console.log(`index: ${i}, value: ${arr[i]}`) } } traverseArray(arr)
Easy enough! 如果用函數式編程的思維重寫上面的代碼應該是這個樣子的:
function forEach(arr, fn) { for (let i = 0; i < arr.length; i++) { fn(i, arr[i], arr) } } forEach(arr, (index, value) => console.log(`index: ${index}, value: ${value}`))
WTF!? 說好的函數式編程更簡潔代碼量更少呢! 明顯第二種寫法寫的代碼更多啊. 別急, 其實es5的時候JS已經把一系列高階函數設置好了,比如forRach, map, reduce, filter, every.. 讓我們用js的array.forEach重寫上面的代碼:
arr.forEach((v, k) => console.log(`index: ${k}, value: ${v}`))
怎么樣, 一樣代碼解決戰斗。 也許看到這里你還是覺得沒什么意思,畢竟雖然代碼量少了些,但也沒感覺有什么本質性的變化. 下面我會舉幾個比較常見的例子,通過例子讓我們一起體會這種有趣的編程思想,也許看過之后你的想法會有所改變.
常用方法舉例先介紹下幾個常用的函數, filter, map, reduce:
var animals = [ {name: "a" , species: "dog", weight: 11}, {name: "b", species: "cat", weight: 10}, {name: "c", species: "fish", weight: 1}, {name: "d", species: "cat", weight: 8}, {name: "e", species: "rabbit", weight: 3} ] // 找到所有種類為貓的動物 animals.filter((animal) => animal.species === "cat") // [ { name: "b", species: "cat" }, { name: "d", species: "cat" } ] // 返回所有動物的名字 animals.map((animal) => animal.name) // [ "a", "b", "c", "d", "e" ] // 動物總重 animals.reduce((pre, animal) => pre + animal.weight, 0) //33
怎么樣? 如果用循環來寫肯定不可能一行代碼就搞定吧. 讓我們看一些更深入更有意思的例子吧.
比如這道比較經典的算法題: 給定一個字符串"abcdaabc"統計每個字符的出現次數.
// 一般做法是這樣的 var str="abcdaabc" var count = {}; var i; for(i=0;i(pre[cur]++ || (pre[cur] = 1), pre), {})
由上面的例子可見, 高階函數的特性讓我們在處理這類問題時比普通的方法要方便的多. 在更實際的應用場景中函數式編程的便利性更能充分的體現, 其中一個經典的例子是統計文本中出現頻率最高的十個單詞:
var fs = require("fs"); var content = fs.readFileSync("words.txt").toString(); var words = content.split(/[s.,/: ]+/); // 把單詞全部變為小寫并利用上一個例子的方法統計單詞出現的次數 var tally = words.map((word) => word.toLowerCase()) .reduce((pre, cur) => (pre[cur]++ || (pre[cur]=1), pre), {}) //把object的key變成數組并進行排序 var top10 = Object.keys(tally) .map((key) => { return {word: key, count: tally[key]} }) .sort((a, b) => b.count - a.count) .slice(0, 10) console.log(top10)
上面這個例子充分體現了函數式編程思想的魅力,代碼精簡并且可讀性高! 想一想如果你要用循環來寫有多多少行代碼!
好了, 總結完畢, 最后附上參考資料.
Functional programming in JavaScript
Functional Programming By Example
Eloquent javascript - chapter6
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86389.html
摘要:前言最近花了不少時間接觸學習的函數式的編程方式,而后為了加深理解,又去折騰。不過幸運的是,天生具備了函數式編程的基本元素,所以學習的起點不會太低。初接觸第一個實例,函數式編程是如何做一個番茄炒雞蛋的。 前言 最近花了不少時間接觸學習javascript的函數式的編程方式,而后為了加深理解,又去折騰haskell。 不同于人們比較熟悉的命令式編程,如面向對象編程(oop),函數式編程(f...
摘要:參考鏈接面向對象編程模型現在的很多編程語言基本都具有面向對象的思想,比如等等,而面向對象的主要思想對象,類,繼承,封裝,多態比較容易理解,這里就不多多描述了。 前言 在我們的日常日發和學習生活中會常常遇到一些名詞,比如 命令式編程模型,聲明式編程模型,xxx語言是面向對象的等等,這個編程模型到處可見,但是始終搞不清是什么?什么語言又是什么編程模型,當你新接觸一門語言的時候,有些問題是需...
摘要:為此決定自研一個富文本編輯器。例如當要轉化的對象有環存在時子節點屬性賦值了父節點的引用,為了關于函數式編程的思考作者李英杰,美團金融前端團隊成員。只有正確使用作用域,才能使用優秀的設計模式,幫助你規避副作用。 JavaScript 專題之惰性函數 JavaScript 專題系列第十五篇,講解惰性函數 需求 我們現在需要寫一個 foo 函數,這個函數返回首次調用時的 Date 對象,注意...
閱讀 3939·2021-10-09 09:43
閱讀 2871·2021-10-08 10:05
閱讀 2734·2021-09-08 10:44
閱讀 882·2019-08-30 15:52
閱讀 2809·2019-08-26 17:01
閱讀 3016·2019-08-26 13:54
閱讀 1650·2019-08-26 10:48
閱讀 807·2019-08-23 14:41