如何使用函數(shù)式TypeScript代碼

本篇文章給大家分享的是有關(guān)如何使用函數(shù)式TypeScript代碼,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、重慶小程序開發(fā)公司、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了三門免費(fèi)建站歡迎大家使用!

談到函數(shù)式編程時(shí),我們常提到機(jī)制、方法,而不是核心原則。函數(shù)式編程不是關(guān)于 Monad、Monoid 和 Zipper  這些概念的,雖然它們確實(shí)很有用。從根本上來(lái)說(shuō),函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進(jìn)行組合編程。本文是我在重構(gòu) TypeScript  代碼時(shí)使用函數(shù)式的一些思考的結(jié)果。

首先,我們需要用到以下幾項(xiàng)技術(shù):

  • 盡可能使用函數(shù)代替簡(jiǎn)單值

  • 數(shù)據(jù)轉(zhuǎn)換過(guò)程管道化

  • 提取通用函數(shù)

來(lái),開始吧!

假設(shè)我們有兩個(gè)類,Employee 和 Department。Employee 有 name 和 salary 屬性,Department 只是  Employee 的簡(jiǎn)單集合。

class Employee {   constructor(public name: string, public salary: number) {} }  class Department {   constructor(public employees: Employee[]) {}    works(employee: Employee): boolean {     return this.employees.indexOf(employee) > -1;   } }

我們要重構(gòu)的是 averageSalary 函數(shù)。

function averageSalary(employees: Employee[], minSalary: number, department?: Department): number {    let total = 0;    let count = 0;     employees.forEach((e) => {      if(minSalary <= e.salary && (department === undefined || department.works(e))){        total += e.salary;        count += 1;      }    });    return total === 0 ? 0 : total / count;  }

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

該函數(shù)的使用方式如下:

describe("average salary", () => {   const empls = [     new Employee("Jim", 100),     new Employee("John", 200),     new Employee("Liz", 120),     new Employee("Penny", 30)   ];    const sales = new Department([empls[0], empls[1]]);    it("calculates the average salary", () => {     expect(averageSalary(empls, 50, sales)).toEqual(150);     expect(averageSalary(empls, 50)).toEqual(140);   }); });

需求雖簡(jiǎn)單粗暴,可就算不提代碼難以拓展,其混亂是顯而易見的。若新增條件,函數(shù)簽名及接口就不得不發(fā)生變動(dòng),if 語(yǔ)句也會(huì)也越來(lái)越臃腫可怕。

我們一起用一些函數(shù)式編程的辦法重構(gòu)這個(gè)函數(shù)吧。

使用函數(shù)代替簡(jiǎn)單值

使用函數(shù)代替簡(jiǎn)單值看起來(lái)似乎不太直觀,但這卻是整理歸納代碼的強(qiáng)大辦法。在我們的例子中,這樣做,意味著要將 minSalary 和 department  參數(shù)替換成兩個(gè)條件檢驗(yàn)的函數(shù)。

type Predicate = (e: Employee) => boolean;  function averageSalary(employees: Employee[], salaryCondition: Predicate,   departmentCondition?: Predicate): number {   let total = 0;   let count = 0;    employees.forEach((e) => {     if(salaryCondition(e) && (departmentCondition === undefined || departmentCondition(e))){       total += e.salary;       count += 1;     }   });    return total === 0 ? 0 : total / count; }  // ...  expect(averageSalary(empls, (e) => e.salary > 50, (e) => sales.works(e))).toEqual(150);

我們所做的就是將 salary、department  兩個(gè)條件接口統(tǒng)一起來(lái)。而此前這兩個(gè)條件是寫死的,現(xiàn)在它們被明確定義了,并且遵循一致的接口。這次整合允許我們將所有條件作為數(shù)組傳遞。

function averageSalary(employees: Employee[], conditions: Predicate[]): number {   let total = 0;   let count = 0;    employees.forEach((e) => {     if(conditions.every(c => c(e))){       total += e.salary;       count += 1;     }   });   return (count === 0) ? 0 : total / count; }  //...  expect(averageSalary(empls, [(e) => e.salary > 50, (e) => sales.works(e)])).toEqual(150);

條件數(shù)組只不過(guò)是組合的條件,可以用一個(gè)簡(jiǎn)單的組合器將它們放到一起,這樣看起來(lái)更加明晰。

function and(predicates: Predicate[]): Predicate {   return (e) => predicates.every(p => p(e)); }  function averageSalary(employees: Employee[], conditions: Predicate[]): number {   let total = 0;   let count = 0;    employees.forEach((e) => {     if(and(conditions)(e)){       total += e.salary;       count += 1;     }   });   return (count == 0) ? 0 : total / count; }

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

提起結(jié)果

現(xiàn)在,averageSalary 函數(shù)已健壯得多了。我們可以加入新條件,無(wú)需破壞函數(shù)接口或改變函數(shù)實(shí)現(xiàn)。

數(shù)據(jù)轉(zhuǎn)換過(guò)程管道化

函數(shù)式編程的另外一個(gè)很有用的實(shí)踐是將所有數(shù)據(jù)轉(zhuǎn)換過(guò)程變成管道。在本例中,就是將 filter 過(guò)程提取到循環(huán)外面。

function averageSalary(employees: Employee[], conditions: Predicate[]): number {   const filtered = employees.filter(and(conditions));    let total = 0   let count = 0    filtered.forEach((e) => {     total += e.salary;     count += 1;   });    return (count == 0) ? 0 : total / count; }

這樣一來(lái)計(jì)數(shù)的 count 就沒什么用了。

function averageSalary(employees: Employee[], conditions: Predicate[]): number{   const filtered = employees.filter(and(conditions));    let total = 0   filtered.forEach((e) => {     total += e.salary;   });    return (filtered.length == 0) ? 0 : total / filtered.length; }

接下來(lái),如在疊加之前將 salary 摘取出來(lái),求和過(guò)程就變成簡(jiǎn)單的 reduce 了。

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

提取通用函數(shù)

接著我們發(fā)現(xiàn),***兩行代碼和當(dāng)前域完全沒什么關(guān)系。其中不包含任何與員工、部門相關(guān)的信息。僅僅只是一個(gè)計(jì)算平均數(shù)的函數(shù)。所以也將其提取出來(lái)。

function average(nums: number[]): number {   const total = nums.reduce((a,b) => a + b, 0);   return (nums.length == 0) ? 0 : total / nums.length; }  function averageSalary(employees: Employee[], conditions: Predicate[]): number {   const filtered = employees.filter(and(conditions));   const salaries = filtered.map(e => e.salary);   return average(salaries); }

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

將所有 salary 部分提出來(lái)之后,我們得到解決方案。

function employeeSalaries(employees: Employee[], conditions: Predicate[]): number[] {   const filtered = employees.filter(and(conditions));   return filtered.map(e => e.salary); }  function averageSalary(employees: Employee[], conditions: Predicate[]): number {   return average(employeeSalaries(employees, conditions)); }

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

何時(shí)收手

函數(shù)式風(fēng)格的編程中,我們會(huì)編寫許多小型函數(shù),它們接收一個(gè)集合,返回新的集合。這些函數(shù)能夠以不同方式組合、復(fù)用 &mdash;&mdash;  棒極了。不過(guò),這種風(fēng)格的一個(gè)缺點(diǎn)是代碼可能會(huì)變得過(guò)度抽象,導(dǎo)致難以讀懂,那些函數(shù)組合在一起到底要干嘛?

我喜歡使用樂高來(lái)類比:樂高積木能夠以不同形式放在一起 ;  它們是可組合的。但注意,并不是所有積木都是一小塊。所以,在使用本文所述技巧進(jìn)行代碼重構(gòu)時(shí),千萬(wàn)別妄圖將一切都變成接收數(shù)組、返回?cái)?shù)組的函數(shù)。誠(chéng)然,這樣一些函數(shù)組合使用極度容易,可它們也會(huì)顯著降低我們對(duì)程序的理解能力。

這里展示了如何使用函數(shù)式思維重構(gòu) TypeScript 代碼。我所遵循的是以下幾點(diǎn)規(guī)則:

  • 盡可能使用函數(shù)代替簡(jiǎn)單值

  • 數(shù)據(jù)轉(zhuǎn)換過(guò)程管道化

  • 提取通用函數(shù)

以上就是如何使用函數(shù)式TypeScript代碼,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)站欄目:如何使用函數(shù)式TypeScript代碼
文章位置:http://muchs.cn/article40/gphjeo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站營(yíng)銷、網(wǎng)站收錄、網(wǎng)站導(dǎo)航、做網(wǎng)站、軟件開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁(yè)設(shè)計(jì)公司