精品欧美一区二区三区在线观看 _久久久久国色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精品国产99久久久久久白柏| 国产精品成人久久久久| 久久中文免费视频| 最新亚洲精品| 欧美本精品男人aⅴ天堂| 欧美v在线观看| 快射av在线播放一区| 播五月开心婷婷综合| 国产精品视频在线播放| 日本三级片在线观看| 日韩理论电影院| 亚洲成人a级网| mm131亚洲精品| 樱花草涩涩www在线播放| 国产精品久久久久久户外露出 | 999在线免费视频| 暖暖在线中文免费日本| 亚洲国产高清aⅴ视频| 国产精品日韩二区| 国产一区二区视频免费观看| 亚洲一区欧美激情| 欧美黑人性猛交| 亚洲欧美卡通动漫| 亚州国产精品| 精品美女在线播放| 欧美精品色视频| 激情中国色综合| 色久综合一二码| 欧美一级视频免费看| 影音先锋在线视频| 国产精品激情偷乱一区二区∴| 久久艹中文字幕| 隣の若妻さん波多野结衣| 激情久久五月天| 国产精品视频网站| 伊人久久中文字幕| 久久男女视频| 热99精品只有里视频精品| 国产精品美女毛片真酒店| 亚洲网色网站| 久久久久北条麻妃免费看| 亚洲毛片亚洲毛片亚洲毛片| 伊人久久综合影院| 亚洲免费精彩视频| 国产a级黄色片| 成人激情自拍| 精品国产污网站| av漫画在线观看| a级日韩大片| 精品久久久久久久久久久久久久久 | 日韩在线短视频| 色88888久久久久久影院野外| 国产精品丝袜久久久久久消防器材| h片在线观看| 亚洲成a人片在线观看中文| 日本男女交配视频| 波多野结衣在线观看| 午夜视频在线观看一区二区| 日韩a∨精品日韩在线观看| а√天堂8资源中文在线| 亚洲成av人**亚洲成av**| 鲁一鲁一鲁一鲁一色| 蜜桃av在线| 色av成人天堂桃色av| 狠狠热免费视频| 日韩午夜视频在线| 日韩欧美亚洲国产精品字幕久久久| 国内精品国产三级国产aⅴ久| 综合视频一区| 亚洲精品黄网在线观看| 中文字幕第4页| 91欧美大片| 久久久久久久网站| 天堂av免费在线观看| 久久成人免费网| av成人午夜| 日本中文字幕电影在线观看 | 91免费视频黄| 欧美xxxx少妇| 一本大道久久a久久综合婷婷 | 国产91丝袜在线播放九色| 激情小说综合网| porn视频在线观看| 一区二区三区欧美久久| 青青视频在线播放| 亚洲电影二区| 亚洲第一男人天堂| 久久视频精品在线观看| 欧美国产日本| 国产精品av免费在线观看| 国产又粗又大又爽| 99久久精品国产一区二区三区| 欧美精品一区三区在线观看| 国产激情在线视频| 黑人巨大精品欧美一区二区免费| 亚洲一区日韩精品| 欧美色资源站| 久久久精品电影| 国产精品乱码一区二区视频| 国产精品夜夜嗨| 日本成人黄色免费看| 亚洲精品一线| 欧美私模裸体表演在线观看| 亚洲av成人精品一区二区三区 | 日韩中文字幕第一页| 国产无遮挡裸体免费视频| 全部av―极品视觉盛宴亚洲| 国产一区二区不卡视频| 九义人在线观看完整免费版电视剧| 天天综合日日夜夜精品| 五月天丁香花婷婷| 精品大片一区二区| 国产91精品不卡视频| 精品国产免费无码久久久| 国产日韩欧美精品在线| 成人性免费视频| 国产专区精品| 搡老女人一区二区三区视频tv| 羞羞影院体验区| 国产精品白丝av| 婷婷久久青草热一区二区 | 欧美日韩三级| 成人乱色短篇合集| 夜级特黄日本大片_在线| 偷窥少妇高潮呻吟av久久免费| 日韩视频在线观看一区二区三区| 精品一区二区三区的国产在线观看| 91av在线网站| 污视频网站在线播放| 一区二区三区国产精品| 亚洲一区二区三区观看| 欧美gayvideo| 国产日韩亚洲欧美| 97超碰人人在线| 欧美亚洲一区二区在线| 国产中年熟女高潮大集合| 久久激情综合| 欧美亚洲丝袜| 欧美最新精品| 在线视频精品一| 欧美日韩a v| 欧美高清在线一区二区| 91看片在线免费观看| 精品黄色一级片| 国产精品久久99久久| 日韩精品毛片| 这里只有精品99re| 亚洲成人生活片| 国产成人精品亚洲777人妖| 国产高清不卡无码视频| 一区二区中文字幕在线观看| 欧美黑人一区二区三区| 色婷婷av一区二区三区之e本道| 亚洲韩国一区二区三区| 国产一级二级在线观看| 可以看av的网站久久看| 天天综合色天天综合色hd| 色999韩欧美国产综合俺来也| 久久深夜福利免费观看| 99久久亚洲精品日本无码| 亚洲精品欧美在线| 高清中文字幕mv的电影| 亚洲一区二区成人| 亚洲区一区二区三区| www.久久热| 韩国精品久久久999| 理论视频在线| 欧美欧美欧美欧美| 精品无码人妻一区二区三区品| av亚洲精华国产精华精华| 国产女女做受ⅹxx高潮| 99国产精品一区二区| 91精品久久久久久蜜桃| 久久男人天堂| 最新69国产成人精品视频免费| 国产特黄一级片| 精品国产乱码久久久久久虫虫漫画 | 黄色污污视频软件| 亚洲男人的天堂av| 在线观看日韩精品视频| 久久精品国产一区二区| 国产精品啪啪啪视频| 美女av一区| 国产日韩欧美自拍| 草草在线观看| 久久好看免费视频| 五月天激情婷婷| 欧美精品一二三四| 97久久久久久久| 日韩一区在线播放| 免费成人蒂法网站| 久久99精品久久久久久久久久久久 | 双性尿奴穿贞c带憋尿| 九一九一国产精品| 亚洲欧洲日产国码无码久久99 | 国产又黄又大又粗视频| 亚洲人metart人体| 欧美午夜欧美| 久久久免费毛片| 91精品综合视频| 欧美日韩不卡| 51精品在线观看| 日韩影视在线| 久久精品视频在线观看| 人成在线免费视频| 精品国产一区二区三区久久久蜜月| 久久久久精彩视频| 欧美日韩亚洲激情| 青青草手机视频在线观看| 中文字幕精品—区二区四季| 精品无码在线视频| 成人午夜免费av| 欧美国产日韩在线视频| 欧美aaaaa成人免费观看视频| 波多野结衣家庭教师在线| 欧美va亚洲va日韩∨a综合色| 亚洲视频导航| 九一国产精品| 麻豆av一区二区三区| av在线亚洲色图| 91久色国产| 国产午夜久久av| 91精品久久久久久久久久另类| 美女100%一区| 91成人性视频| 麻豆国产在线| 66m—66摸成人免费视频| 操喷在线视频| 国产69精品99久久久久久宅男| a级影片在线观看| 两个人的视频www国产精品| 视频免费一区| 日韩在线小视频| 色欧美激情视频在线| 在线亚洲国产精品网| 成人高清网站| 在线观看日韩av| av在线三区| 日韩在线小视频| 激情在线小视频| 久久精品久久久久久国产 免费| 日本视频在线| 免费97视频在线精品国自产拍| 韩国av网站在线| 欧美理论电影在线观看| 青草在线视频| 高清亚洲成在人网站天堂| aa级大片免费在线观看| 91精品国产成人| 韩日精品一区二区| 国产精品高潮呻吟久久av野狼 | 一本在线免费视频| 国产精品不卡在线| 婷婷社区五月天| 樱桃视频在线观看一区| 久久中文字幕在线观看| 精品久久久久久国产91| 国产又大又粗又爽| 欧美三级中文字幕| 国产人妻精品一区二区三区| 欧美成人性战久久| 日韩porn| 日韩视频在线免费观看| 黄色的视频在线观看| 欧美亚洲国产另类| 成人一区视频| 亚洲自拍偷拍网址| 老牛精品亚洲成av人片| 欧洲视频一区二区三区| 亚洲成人国产| 欧美午夜性视频| 丝袜美腿亚洲色图| 亚洲第一成肉网| 99久久婷婷国产综合精品电影 | 国产日韩视频在线观看| 香蕉免费一区二区三区在线观看 | 女同性αv亚洲女同志| 91在线小视频| 成人无码精品1区2区3区免费看| 亚洲最大的成人av| 99re这里只有精品在线| 在线综合+亚洲+欧美中文字幕| 四季av日韩精品一区| 在线播放国产精品| 女同视频在线观看| 国产精品久久久久久久久男| 91精品导航| 日韩av影视| 亚洲视频高清| 黄色手机在线视频| 99国内精品久久| 色婷婷在线视频观看| 色呦呦国产精品| 黄色av小说在线观看| 中文字幕亚洲专区| 女人高潮被爽到呻吟在线观看| 国产在线999| 亚洲综合图色| 亚洲一区二区三区av无码| 美女尤物国产一区| 97伦伦午夜电影理伦片| 亚洲一区在线观看网站| 亚洲天堂aaa| 精品无人国产偷自产在线| av片在线观看免费| 国产精品激情自拍| 亚洲动漫在线观看| www.日本少妇| 国产精品香蕉一区二区三区| 精品在线观看一区| 一本色道亚洲精品aⅴ| 肥臀熟女一区二区三区| 久久天天躁狠狠躁夜夜av| 中文字幕系列一区| 欧洲av一区| 亚洲一卡久久| 97人妻天天摸天天爽天天| 亚洲一区影音先锋| 国产高清第一页| 久久亚洲国产精品成人av秋霞| 99久久综合国产精品二区| 久热这里只精品99re8久| 99成人免费视频| av免费观看不卡| 亚洲一区二区三区在线播放| 99热这里只有精品1| 欧美成人激情视频| 高清一区二区三区av| 亚洲日本精品| 久久se这里有精品| 国产视频不卡在线| 欧美艳星brazzers| 日日夜夜精品一区| 成人a在线视频| 中文字幕午夜精品一区二区三区| 奇米视频7777| 亚洲美女屁股眼交| 国产高清免费观看| 欧美极品少妇xxxxⅹ喷水| aiai久久| 黄色动漫在线免费看| 91在线一区二区三区| 超碰中文字幕在线| 亚洲欧美日韩网| 69堂精品视频在线播放| 午夜视频久久久| 精品在线观看视频| 亚洲色婷婷一区二区三区| 日韩视频一区二区三区在线播放| av在线官网| 国产乱码精品一区二区三区不卡| 亚洲国内自拍| 性色av蜜臀av色欲av| 色先锋资源久久综合| av影片免费在线观看| 成人精品久久一区二区三区| 欧美三区在线| 在线观看国产网站| 欧美性一级生活| 免费在线看黄色| 国产91精品一区二区绿帽| 99视频一区| 免费成人深夜天涯网站| 欧美精品一卡两卡| www视频在线观看| 亚洲美女搞黄| 国产成人高清视频| 久草视频一区二区| 日韩三级成人av网| 国产精品videossex| 人人爽人人av| 一区二区久久久久| 能在线看的av| 成人欧美一区二区三区在线| 亚洲激情婷婷| 欧美性受xxxx黑人| 欧美大胆人体bbbb| 日韩欧美一区二区三区免费观看| 伊人天天久久大香线蕉av色| 成人av先锋影音| 中文字幕在线观看1| 欧美激情中文网| 久久伦理在线| 日本japanese极品少妇| 欧美日韩视频在线观看一区二区三区 | 亚洲图片欧美另类| 欧美三级韩国三级日本三斤| 欧美午夜大胆人体| 视频一区三区| 成人av免费在线| 国产精品无码在线播放| 日本欧美中文字幕| 激情综合视频| 男人的午夜天堂|