精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

[譯] 函數式TypeScript

開發 前端
談到函數式編程時,我們常提到機制、方法,而不是核心原則。函數式編程不是關于 Monad、Monoid 和 Zipper 這些概念的,雖然它們確實很有用。從根本上來說,函數式編程就是關于如使用通用的可復用函數進行組合編程。本文是我在重構 TypeScript 代碼時使用函數式的一些思考的結果。

[[172673]]

談到函數式編程時,我們常提到機制、方法,而不是核心原則。函數式編程不是關于 Monad、Monoid 和 Zipper 這些概念的,雖然它們確實很有用。從根本上來說,函數式編程就是關于如使用通用的可復用函數進行組合編程。本文是我在重構 TypeScript 代碼時使用函數式的一些思考的結果。

首先,我們需要用到以下幾項技術:

  • 盡可能使用函數代替簡單值
  • 數據轉換過程管道化
  • 提取通用函數

來,開始吧!

假設我們有兩個類,Employee 和 Department。Employee 有 name 和 salary 屬性,Department 只是 Employee 的簡單集合。

  1. class Employee { 
  2.   constructor(public name: string, public salary: number) {} 
  3.  
  4. class Department { 
  5.   constructor(public employees: Employee[]) {} 
  6.  
  7.   works(employee: Employee): boolean { 
  8.     return this.employees.indexOf(employee) > -1; 
  9.   } 
  10.  

我們要重構的是 averageSalary 函數。

  1. function averageSalary(employees: Employee[], minSalary: number, department?: Department): number { 
  2.    let total = 0; 
  3.    let count = 0; 
  4.  
  5.    employees.forEach((e) => { 
  6.      if(minSalary <= e.salary && (department === undefined || department.works(e))){ 
  7.        total += e.salary; 
  8.        count += 1; 
  9.      } 
  10.    }); 
  11.  
  12.   return total === 0 ? 0 : total / count
  13.  }  

averageSalary 函數接收 employee 數組、***薪資 minSalary 以及可選的 department 作為參數。如果傳了 department 參數,函數會計算該部門中所有員工的平均薪資;若不傳,則對全部員工進行計算。

該函數的使用方式如下: 

  1. describe("average salary", () => { 
  2.   const empls = [ 
  3.     new Employee("Jim", 100), 
  4.     new Employee("John", 200), 
  5.     new Employee("Liz", 120), 
  6.     new Employee("Penny", 30) 
  7.   ]; 
  8.  
  9.   const sales = new Department([empls[0], empls[1]]); 
  10.  
  11.   it("calculates the average salary", () => { 
  12.     expect(averageSalary(empls, 50, sales)).toEqual(150); 
  13.     expect(averageSalary(empls, 50)).toEqual(140); 
  14.   }); 
  15. });  

需求雖簡單粗暴,可就算不提代碼難以拓展,其混亂是顯而易見的。若新增條件,函數簽名及接口就不得不發生變動,if 語句也會也越來越臃腫可怕。

我們一起用一些函數式編程的辦法重構這個函數吧。

使用函數代替簡單值

使用函數代替簡單值看起來似乎不太直觀,但這卻是整理歸納代碼的強大辦法。在我們的例子中,這樣做,意味著要將 minSalary 和 department 參數替換成兩個條件檢驗的函數。 

  1. type Predicate = (e: Employee) => boolean; 
  2.  
  3. function averageSalary(employees: Employee[], salaryCondition: Predicate, 
  4.   departmentCondition?: Predicate): number { 
  5.   let total = 0; 
  6.   let count = 0; 
  7.  
  8.   employees.forEach((e) => { 
  9.     if(salaryCondition(e) && (departmentCondition === undefined || departmentCondition(e))){ 
  10.       total += e.salary; 
  11.       count += 1; 
  12.     } 
  13.   }); 
  14.  
  15.   return total === 0 ? 0 : total / count
  16.  
  17. // ... 
  18.  
  19. expect(averageSalary(empls, (e) => e.salary > 50, (e) => sales.works(e))).toEqual(150);  

我們所做的就是將 salary、department 兩個條件接口統一起來。而此前這兩個條件是寫死的,現在它們被明確定義了,并且遵循一致的接口。這次整合允許我們將所有條件作為數組傳遞。 

  1. function averageSalary(employees: Employee[], conditions: Predicate[]): number { 
  2.   let total = 0; 
  3.   let count = 0; 
  4.  
  5.   employees.forEach((e) => { 
  6.     if(conditions.every(c => c(e))){ 
  7.       total += e.salary; 
  8.       count += 1; 
  9.     } 
  10.   }); 
  11.   return (count === 0) ? 0 : total / count
  12.  
  13. //... 
  14.  
  15. expect(averageSalary(empls, [(e) => e.salary > 50, (e) => sales.works(e)])).toEqual(150);  

條件數組只不過是組合的條件,可以用一個簡單的組合器將它們放到一起,這樣看起來更加明晰。 

  1. function and(predicates: Predicate[]): Predicate { 
  2.   return (e) => predicates.every(p => p(e)); 
  3.  
  4. function averageSalary(employees: Employee[], conditions: Predicate[]): number { 
  5.   let total = 0; 
  6.   let count = 0; 
  7.  
  8.   employees.forEach((e) => { 
  9.     if(and(conditions)(e)){ 
  10.       total += e.salary; 
  11.       count += 1; 
  12.     } 
  13.   }); 
  14.   return (count == 0) ? 0 : total / count
  15.  

值得注意的是,“and” 組合器是通用的,可以復用并且還可能拓展為庫。

提起結果

現在,averageSalary 函數已健壯得多了。我們可以加入新條件,無需破壞函數接口或改變函數實現。

數據轉換過程管道化

函數式編程的另外一個很有用的實踐是將所有數據轉換過程變成管道。在本例中,就是將 filter 過程提取到循環外面。 

  1. function averageSalary(employees: Employee[], conditions: Predicate[]): number { 
  2.   const filtered = employees.filter(and(conditions)); 
  3.  
  4.   let total = 0 
  5.   let count = 0 
  6.  
  7.   filtered.forEach((e) => { 
  8.     total += e.salary; 
  9.     count += 1; 
  10.   }); 
  11.  
  12.   return (count == 0) ? 0 : total / count
  13.  

這樣一來計數的 count 就沒什么用了。 

  1. function averageSalary(employees: Employee[], conditions: Predicate[]): number{ 
  2.   const filtered = employees.filter(and(conditions)); 
  3.  
  4.   let total = 0 
  5.   filtered.forEach((e) => { 
  6.     total += e.salary; 
  7.   }); 
  8.  
  9.   return (filtered.length == 0) ? 0 : total / filtered.length; 
  10.  

接下來,如在疊加之前將 salary 摘取出來,求和過程就變成簡單的 reduce 了。 

  1. function averageSalary(employees: Employee[], conditions: Predicate[]): number { 
  2.   const filtered = employees.filter(and(conditions)); 
  3.   const salaries = filtered.map(e => e.salary); 
  4.  
  5.   const total = salaries.reduce((a,b) => a + b, 0); 
  6.   return (salaries.length == 0) ? 0 : total / salaries.length; 
  7.  

提取通用函數

接著我們發現,***兩行代碼和當前域完全沒什么關系。其中不包含任何與員工、部門相關的信息。僅僅只是一個計算平均數的函數。所以也將其提取出來。 

  1. function average(nums: number[]): number { 
  2.   const total = nums.reduce((a,b) => a + b, 0); 
  3.   return (nums.length == 0) ? 0 : total / nums.length; 
  4.  
  5. function averageSalary(employees: Employee[], conditions: Predicate[]): number { 
  6.   const filtered = employees.filter(and(conditions)); 
  7.   const salaries = filtered.map(e => e.salary); 
  8.   return average(salaries); 
  9.  

又一次,提取出的函數是完全通用的。

***,將所有 salary 部分提出來之后,我們得到***方案。 

  1. function employeeSalaries(employees: Employee[], conditions: Predicate[]): number[] { 
  2.   const filtered = employees.filter(and(conditions)); 
  3.   return filtered.map(e => e.salary); 
  4.  
  5. function averageSalary(employees: Employee[], conditions: Predicate[]): number { 
  6.   return average(employeeSalaries(employees, conditions)); 
  7.  

對比原始方案和***方案,我敢說,毫無疑問,后者更棒。首先,它更通用(我們可以不破壞函數接口的情況下添加新類型的判斷條件)。其次,我們從可變狀態(mutable state)和 if 語句中解脫出來,這使代碼更容易閱讀、理解。

何時收手

函數式風格的編程中,我們會編寫許多小型函數,它們接收一個集合,返回新的集合。這些函數能夠以不同方式組合、復用 —— 棒極了。不過,這種風格的一個缺點是代碼可能會變得過度抽象,導致難以讀懂,那些函數組合在一起到底要干嘛?

我喜歡使用樂高來類比:樂高積木能夠以不同形式放在一起 —— 它們是可組合的。但注意,并不是所有積木都是一小塊。所以,在使用本文所述技巧進行代碼重構時,千萬別妄圖將一切都變成接收數組、返回數組的函數。誠然,這樣一些函數組合使用極度容易,可它們也會顯著降低我們對程序的理解能力。

小結

本文展示了如何使用函數式思維重構 TypeScript 代碼。我所遵循的是以下幾點規則:

  • 盡可能使用函數代替簡單值
  • 數據轉換過程管道化
  • 提取通用函數

了解更多

強烈推薦以下兩本書:

關注 @victorsavkin 獲得更多關于 Angular 和 TypeScript 的知識。

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2016-10-11 13:32:50

函數式TypeScriptJavascript

2016-11-04 13:00:55

Asynces6Javascript

2010-01-28 14:51:24

Scala后函數式

2022-01-04 19:21:04

函數TypeScript重載

2023-05-16 16:03:10

2022-02-25 09:19:32

TypeScript輔助函數枚舉

2023-04-14 15:44:20

TypeScrip函數重載

2019-09-09 11:40:18

編程函數開發

2013-09-09 09:41:34

2023-08-24 16:24:44

TypeScript

2017-06-08 14:25:46

Kotlin函數

2012-03-14 10:09:51

ibmdw

2013-07-09 09:43:04

函數式思維函數式編程編程

2014-09-05 10:15:41

函數式編程

2012-11-01 11:33:55

IBMdw

2020-09-24 10:57:12

編程函數式前端

2025-03-11 10:00:20

Golang編程函數

2016-10-31 20:46:22

函數式編程Javascript

2011-03-08 15:47:32

函數式編程

2022-07-07 09:03:36

Python返回函數匿名函數
點贊
收藏

51CTO技術棧公眾號

99综合电影在线视频| 欧美丰满老妇| 欧美综合一区二区| 亚洲成人在线视频网站| 97国产成人无码精品久久久| 欧美人与禽猛交乱配视频| 亚洲国产欧美一区| 青青青在线视频免费观看| 国产欧美黑人| 97精品国产97久久久久久久久久久久| 国产精品久久久久久久电影| 国产精品白嫩白嫩大学美女| 日韩啪啪网站| 日韩一级成人av| 99久久国产宗和精品1上映| www在线观看播放免费视频日本| 成人高清伦理免费影院在线观看| 国产精品一区二区电影| 日韩成年人视频| 91欧美日韩| 亚洲黄色片网站| 91热视频在线观看| 成人日韩精品| 欧美日韩一二三四五区| 日韩亚洲欧美一区二区| www在线播放| 91在线视频18| 成人一区二区在线| 国产成人精品毛片| 久久99精品视频| 国产精品av网站| 日韩女优在线观看| 国产精品99一区二区| 久久精品国产99国产精品澳门 | 久久精品人人爽| 国产精品久久久久无码av色戒| 91精品入口| 欧美久久一区二区| 国内自拍视频网| 自拍偷拍欧美视频| 激情av一区二区| 欧美又粗又长又爽做受| 欧美性受ⅹ╳╳╳黑人a性爽| 国产精品久久久久四虎| 三区精品视频| 国产精品久久久久一区二区国产| 久久综合狠狠综合| 欧美日韩免费高清| 日韩精品123| 久久久久久久久蜜桃| 免费不卡亚洲欧美| 男男电影完整版在线观看| 99久久伊人网影院| 久久精品国产第一区二区三区最新章节 | 日韩伦理在线免费观看| 午夜dj在线观看高清视频完整版| 亚洲男女一区二区三区| 国产精品久久成人免费观看| 老司机99精品99| 亚洲视频中文字幕| 成人性做爰片免费视频| 成人在线观看亚洲| 亚洲激情第一区| 欧美这里只有精品| 乱馆动漫1~6集在线观看| 欧美性生交xxxxx久久久| 国产高清精品在线观看| 欧美日韩视频网站| 欧美性猛交xxxxxx富婆| 奇米视频7777| 亚洲国产精品免费视频| 精品国产区一区| 熟女人妻在线视频| 精品国产欧美日韩| www.久久撸.com| 免费在线视频观看| 99综合视频| 国产精品27p| 伊人色综合久久久| 韩国v欧美v亚洲v日本v| 国产91精品一区二区绿帽| 欧洲成人一区二区三区| 久久精品人人做人人爽人人| 亚洲图片都市激情| 免费看电影在线| 色婷婷综合激情| 麻豆网站免费观看| 日韩欧美影院| 久久久av网站| 国产一级片毛片| 久久99久久99小草精品免视看| 成人免费视频网站| 国产中文字幕在线观看| 亚洲日本护士毛茸茸| a在线视频观看| 欧美视频在线视频精品| 精品国产百合女同互慰| av永久免费观看| 欧美精品不卡| 国产97色在线| 国产成人无码www免费视频播放| 久久综合九色综合欧美亚洲| 成人在线观看www| 亚洲v.com| 日韩精品一区二区三区视频| 成人无码av片在线观看| 狠狠色综合网| 成人免费视频在线观看超级碰| 凸凹人妻人人澡人人添| 一区二区三区中文字幕电影| 国产精品亚洲二区在线观看| 91亚洲无吗| 日韩在线www| 无码人妻熟妇av又粗又大| 国产精品一级在线| 亚洲欧美日本国产有色| 热色播在线视频| 欧美岛国在线观看| 三级黄色在线观看| 欧美a一区二区| 国产激情一区二区三区在线观看 | 97在线精品国自产拍中文| 91在线你懂的| 国产精品无遮挡| 国产福利视频在线播放| 极品国产人妖chinesets亚洲人妖 激情亚洲另类图片区小说区 | 欧美私模裸体表演在线观看| 国产福利短视频| 亚洲性感美女99在线| 亚洲xxxxx| 免费看美女视频在线网站| 欧美日韩中文精品| 国产又大又粗又爽的毛片| 亚洲欧美bt| 久久久久久久久久久久久久一区| 日本大胆在线观看| 4438x成人网最大色成网站| 精品人妻中文无码av在线| 久久激情综合| 久久国产精品精品国产色婷婷| 超碰在线97国产| 欧美mv日韩mv| 日韩免费一二三区| k8久久久一区二区三区| 国产女主播自拍| 国产精品久久久久久久久久白浆| 欧美国产亚洲视频| 俄罗斯嫩小性bbwbbw| 亚洲一级在线观看| 日本wwww色| 国产综合婷婷| 国产一区二区三区色淫影院| 国产中文在线播放| 日韩av在线免费观看| 日韩在线视频免费播放| 久久亚洲一区二区三区四区| 波多野结衣家庭教师视频| 网曝91综合精品门事件在线| 2019日本中文字幕| 日本不卡视频一区二区| 色欧美片视频在线观看| 能直接看的av| 精品亚洲国内自在自线福利| 蜜臀在线免费观看| www.国产精品一区| 91av在线看| www.黄在线观看| 91精品免费观看| 国产主播在线播放| 久久亚洲二区三区| 久热精品在线播放| 国产精品多人| 欧美理论一区二区| 久久麻豆视频| 欧美极品欧美精品欧美视频 | 91女神在线视频| 在线观看高清免费视频| 综合视频在线| 精品国产一区二区三| 韩国精品主播一区二区在线观看 | 青青操免费在线视频| 久久久久久久性| 一区二区三区四区毛片| 影院欧美亚洲| 久久精品亚洲热| 毛片精品免费在线观看| 特黄特色免费视频| 国产日本精品| 亚洲国产精品久久久久久女王| 偷拍自拍亚洲| 68精品国产免费久久久久久婷婷| 精品资源在线看| 欧美一区二区三区爱爱| www.国产com| 亚洲欧美一区二区三区极速播放| 成人免费毛片日本片视频| 美女视频免费一区| 18岁网站在线观看| 久久99久久久| 99热这里只有精品1| www成人免费观看| 欧美日韩1区| 国产成+人+综合+亚洲欧洲| 操你啦视频在线| 亚洲欧美国产精品久久久久久久 | 欧美日韩不卡一区| 日韩精品一区二区在线播放| 国产精品久久久久久久久免费相片| 91人人澡人人爽| 老司机精品视频在线| www黄色日本| 欧美在线高清| 亚洲欧美日韩精品久久久 | 亚洲一区精彩视频| 日韩精品丝袜美腿| 国产不卡一区二区三区在线观看| 久久婷婷五月综合色丁香| 欧美亚洲第一页| 金瓶狂野欧美性猛交xxxx| 久久精品久久久久久国产 免费| 国产51人人成人人人人爽色哟哟| 精品成人一区二区三区四区| 91九色单男在线观看| 丰满岳乱妇国产精品一区| 欧美日韩精品一区二区三区四区| 青青青国产在线| 天天综合日日夜夜精品| 九九免费精品视频| 亚洲日本电影在线| 三级黄色在线观看| 中文字幕一区二区视频| 你懂得视频在线观看| 久久精品免视看| 天堂久久久久久| av不卡免费电影| 国产成人精品无码片区在线| 成人午夜视频在线观看| 国产成人精品一区二区三区在线观看| 精品在线观看视频| 污污的视频免费| 麻豆精品久久精品色综合| 性刺激的欧美三级视频| 免费观看在线色综合| 欧美自拍小视频| 蜜臀久久99精品久久久画质超高清| 精品视频一区二区在线| 麻豆成人在线| 波多野结衣天堂| 美女视频网站久久| 亚洲综合20p| 国产69精品久久777的优势| 丰满少妇xbxb毛片日本| bt7086福利一区国产| 懂色av粉嫩av蜜乳av| 久久久无码精品亚洲日韩按摩| 久操视频免费看| 国产精品理论片在线观看| 91精品国产色综合| 青青色在线视频| 亚洲天堂免费在线| 午夜免费福利在线观看| 久久av资源网站| 金瓶狂野欧美性猛交xxxx| 91成人性视频| 日韩精品第一| 91精品一区二区| 成人免费在线电影网| 久久精品国产精品国产精品污| 亚洲宅男一区| 在线综合视频网站| 欧美精品日本| 人妻少妇被粗大爽9797pw| 免费在线观看精品| 国产精久久久久| 久久久99久久精品欧美| 成人免费毛片xxx| 亚洲高清视频的网址| 午夜精品免费观看| 欧美一区二区大片| 欧美日韩国产综合视频| 久久精品久久久久久| 日本乱码一区二区三区不卡| 国产日韩中文字幕| 久久精品国产亚洲blacked| 日韩欧美亚洲精品| 在线观看一区视频| wwww.国产| 91麻豆.com| www.毛片com| 日本乱人伦aⅴ精品| a在线观看视频| 亚洲人成在线播放| 欧美人与性动交α欧美精品济南到| 欧美一区二区三区四区在线| 91精品国产一区二区在线观看| 久久草视频在线看| 久久精品亚洲欧美日韩精品中文字幕| 久久久亚洲精品无码| 激情欧美日韩一区二区| av网在线播放| 欧美日韩国产综合新一区| 国产精品久久久国产盗摄| 亚洲精品少妇网址| 呦呦在线视频| 国产欧美欧洲在线观看| 偷拍亚洲精品| 日本熟妇人妻xxxx| 国产一区二区三区久久久| 久久久久亚洲av无码a片| 亚洲综合在线第一页| 在线免费一级片| 亚洲人成在线电影| 麻豆mv在线看| 俄罗斯精品一区二区三区| 欧美成人激情| 亚洲国产精品毛片av不卡在线| 99在线精品观看| 激情综合五月网| 亚洲欧洲韩国日本视频| 国产小视频免费| 奇米四色…亚洲| 中文幕无线码中文字蜜桃| 亚洲福利一区二区| 97人人模人人爽人人少妇| 理论片一区二区在线| 黄色片免费在线观看视频| 九一九一国产精品| 国产午夜福利一区| 一本高清dvd不卡在线观看| 韩国av永久免费| 欧美国产日韩一区二区在线观看| 91麻豆精品国产综合久久久| 五月天丁香综合久久国产| 天堂va蜜桃一区二区三区| 人人妻人人澡人人爽人人精品| 午夜精品久久久久久久| 蜜桃av中文字幕| 久久久久国产精品免费| 亚洲一区二区三区免费| 亚洲五码在线观看视频| 国产麻豆精品在线| 一级黄色录像视频| 日韩欧美激情在线| 毛片在线导航| 国产一区二区三区四区五区在线 | 成人午夜视频一区二区播放| 欧美巨猛xxxx猛交黑人97人| 欧美视频三区| 久久久国内精品| 成人一区二区三区中文字幕| 久久一区二区三| 亚洲精品电影网在线观看| 蜜桃麻豆影像在线观看| 欧美精品v日韩精品v国产精品| 久久精品女人| 欧美亚洲色综久久精品国产| 欧美日韩色一区| 天堂av中文在线| 精品国产乱码久久久久久蜜柚| 亚洲深夜av| 天天操天天舔天天射| 欧美日韩国产成人在线免费| 成人直播在线| 精品免费一区二区三区蜜桃| 久久久精品性| 欧美另类videoxo高潮| 日韩欧美亚洲国产另类| 不卡一本毛片| 欧美日韩亚洲一区二区三区四区| 日本欧美一区二区三区乱码 | 亚洲成人av在线电影| 人成在线免费视频| 国产精品入口日韩视频大尺度| 婷婷久久综合| 亚洲一区二区在线免费| 在线观看日韩国产| 在线观看小视频| 久久av一区二区三区亚洲| 美腿丝袜亚洲综合| 久久免费小视频| 亚洲欧美三级伦理| 日韩成人精品| 欧美 日韩 国产一区| 国产精品国产三级国产三级人妇| 亚洲av永久纯肉无码精品动漫| 国产91精品高潮白浆喷水| 色喇叭免费久久综合| 久久免费精品国产| 欧美日韩在线综合| 免费h在线看| 成年人免费观看的视频| 26uuu久久天堂性欧美| 国产精品久久久久久在线| 97香蕉超级碰碰久久免费软件| 国产精品久久久乱弄| 国产精品无码永久免费不卡| 制服.丝袜.亚洲.中文.综合| 在线手机中文字幕| 免费网站永久免费观看| 中日韩av电影|