狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

為什么一定要在團(tuán)隊(duì)項(xiàng)目開發(fā)中去使用 TypeScript ?

admin
2024年11月18日 17:52 本文熱度 565

導(dǎo)讀

這篇文章主要介紹了在團(tuán)隊(duì)項(xiàng)目開發(fā)中使用 TypeScript 的價(jià)值,重點(diǎn)講解了如何用 TypeScript 實(shí)現(xiàn)一個(gè)類型安全的防抖函數(shù)。從函數(shù)框架、參數(shù)添加、定時(shí)器邏輯到使用泛型優(yōu)化、添加 cancel 方法和 JSDoc 注釋等步驟,詳細(xì)闡述了防抖函數(shù)的實(shí)現(xiàn)過程及優(yōu)點(diǎn),強(qiáng)調(diào)了其提升代碼安全性和健壯性的作用。

為什么要去使用 TypeScript ?

一直以來 TypeScript 的存在都備受爭議,很多人認(rèn)為他加重了前端開發(fā)的負(fù)擔(dān),特別是在它的嚴(yán)格類型系統(tǒng)和 JavaScript 的靈活性之間的矛盾上引發(fā)了不少討論。

支持者認(rèn)為 TypeScript 提供了強(qiáng)類型檢查、豐富的 IDE 支持和更好的代碼重構(gòu)能力,從而提高了大型項(xiàng)目的代碼質(zhì)量和可維護(hù)性。

然而,也有很多開發(fā)者認(rèn)為 TypeScript 加重了開發(fā)負(fù)擔(dān),帶來了不必要的復(fù)雜性,尤其是在小型項(xiàng)目或快速開發(fā)場景中,它的嚴(yán)格類型系統(tǒng)可能顯得過于繁瑣,限制了 JavaScript 本身的動態(tài)和自由特性

但是隨著項(xiàng)目規(guī)模的增大和團(tuán)隊(duì)協(xié)作的復(fù)雜性增加,TypeScript 的優(yōu)勢也更加明顯。因?yàn)槟悴豢赡苤竿麍F(tuán)隊(duì)中所有人的知識層次和開發(fā)習(xí)慣都達(dá)到同一水準(zhǔn)!你也不可能保證團(tuán)隊(duì)中的其他人都能夠完全正確的使用你封裝的組件、函數(shù)!

在大型項(xiàng)目中我們往往會封裝到很多工具函數(shù)、組件等等,我們不可能在使用到組件時(shí)跑去看這個(gè)組件的實(shí)現(xiàn)邏輯,而 TypeScript 的類型提示正好彌補(bǔ)了這一點(diǎn)。通過明確的類型注解,TypeScript 可以在代碼中直接提示每個(gè)組件的輸入輸出、參數(shù)類型和預(yù)期結(jié)果,讓開發(fā)者只需在 IDE 中懸停或查看提示信息,就能了解組件的用途和使用方式,而不需要翻閱具體實(shí)現(xiàn)邏輯。

這時(shí)你可能會說,使用 JSDoc 也能夠?qū)崿F(xiàn)類似的效果。的確,JSDoc 可以通過注釋的形式對函數(shù)、參數(shù)、返回值等信息進(jìn)行詳細(xì)描述,甚至可以生成文檔。

然而,JSDoc 依賴于開發(fā)者的自覺維護(hù),且其檢查和提示能力遠(yuǎn)不如 TypeScript 強(qiáng)大和全面。TypeScript 的類型系統(tǒng)是在編譯階段強(qiáng)制執(zhí)行的,這意味著所有類型定義都是真正的 “硬性約束”,能在代碼運(yùn)行前捕獲錯(cuò)誤,而不僅僅是提示。

在實(shí)際開發(fā)中,JSDoc 的確能讓我們知道參數(shù)類型,但它只是一種 “約定” ,而不是真正的約束。這意味著,如果同事在使用工具函數(shù)時(shí)不小心寫錯(cuò)了類型,比如傳了字符串而不是數(shù)字,JSDoc 只能通過注釋告訴你正確的使用方法,卻無法在你出錯(cuò)時(shí)立即給出警告。

然而在 TypeScript 中,類型系統(tǒng)會在代碼編寫階段實(shí)時(shí)檢查。比如,你定義的函數(shù)要求傳入數(shù)字類型的參數(shù),如果有人傳入了字符串,IDE 立刻會報(bào)錯(cuò)提醒你,防止錯(cuò)誤進(jìn)一步傳播。

所以,TypeScript 的價(jià)值就在于它提供了一層代碼保護(hù),讓代碼有了“硬約束”,團(tuán)隊(duì)在開發(fā)過程中更加節(jié)省心智負(fù)擔(dān),顯著提升開發(fā)體驗(yàn)和生產(chǎn)力,少出錯(cuò)、更高效。

接下來我們來使用 TypeScript 寫一個(gè)基礎(chǔ)的防抖函數(shù)作為示例。通過類型定義和參數(shù)注解,我們不僅能讓防抖函數(shù)更加通用且類型安全,還能充分利用 TypeScript 的類型檢查優(yōu)勢,從而提高代碼的可讀性和可維護(hù)性。

這樣的實(shí)現(xiàn)方式將有效地降低潛在的運(yùn)行時(shí)錯(cuò)誤,特別是在大型項(xiàng)目中,可以使團(tuán)隊(duì)成員之間的協(xié)作能夠更加順暢,并且避免一些低級問題。 

?


功能點(diǎn)講解

防抖函數(shù)的主要功能是:在指定的延遲時(shí)間內(nèi),如果函數(shù)多次調(diào)用,只有最后一次調(diào)用會生效。這一功能尤其適合優(yōu)化用戶輸入等高頻事件。

防抖函數(shù)的核心功能

  1. 函數(shù)執(zhí)行的延遲控制:函數(shù)調(diào)用后不立即執(zhí)行,而是等待一段時(shí)間。如果在等待期間再次調(diào)用函數(shù),之前的等待會被取消,重新計(jì)時(shí)。

  2. 立即執(zhí)行選項(xiàng):有時(shí)我們希望函數(shù)在第一次調(diào)用時(shí)立即執(zhí)行,然后在延遲時(shí)間內(nèi)避免再次調(diào)用。

  3. 取消功能:我們還希望在某些情況下手動取消延遲執(zhí)行的函數(shù),比如當(dāng)頁面卸載或需要重新初始化時(shí)。


第一步:編寫函數(shù)框架

在開始封裝防抖函數(shù)之前,我們首先應(yīng)該想到的就是要寫一個(gè)函數(shù),假設(shè)這個(gè)函數(shù)名叫 debounce。我們先創(chuàng)建它的基本框架:

typescript代碼解讀復(fù)制代碼function debounce() {     // 函數(shù)的邏輯將在這里編寫 } 

這一步非常簡單,先定義一個(gè)空函數(shù),這個(gè)函數(shù)就是我們的防抖函數(shù)。在后續(xù)步驟中,我們會逐步向這個(gè)函數(shù)中添加功能。

第二步:添加基本的參數(shù)

防抖函數(shù)的第一個(gè)功能是控制某個(gè)函數(shù)的執(zhí)行,因此,我們需要傳遞一個(gè)需要防抖的函數(shù)。其次,防抖功能依賴于一個(gè)延遲時(shí)間,這意味著我們還需要添加一個(gè)用于設(shè)置延遲的參數(shù)。

讓我們擴(kuò)展一下 debounce 函數(shù),為它添加兩個(gè)基本的參數(shù):

  1. func:需要防抖的目標(biāo)函數(shù)。

  2. duration:防抖的延遲時(shí)間,單位是毫秒。

typescript代碼解讀復(fù)制代碼function debounce(func: Function, duration: number) {     // 函數(shù)的邏輯將在這里編寫 } 
  • func 是需要防抖的函數(shù)。每當(dāng)防抖函數(shù)被調(diào)用時(shí),我們實(shí)際上是在控制這個(gè) func 函數(shù)的執(zhí)行。

  • duration 是延遲時(shí)間。這個(gè)參數(shù)控制了在多長時(shí)間后執(zhí)行目標(biāo)函數(shù)

第三步:為防抖功能引入定時(shí)器邏輯

防抖的核心邏輯就是通過定時(shí)器setTimeout),讓函數(shù)執(zhí)行延后。那么我們需要用一個(gè)變量來保存這個(gè)定時(shí)器,以便在函數(shù)多次調(diào)用時(shí)可以取消之前的定時(shí)器。

typescript代碼解讀復(fù)制代碼function debounce(func: Function, duration: number) {     let timer: ReturnType<typeof setTimeout> | null = null; // 定時(shí)器變量 } 
  • let timer: ReturnType<typeof setTimeout> | null = null:我們使用一個(gè)變量 timer 來存儲定時(shí)器的返回值。

  • clearTimeout(timer):每次調(diào)用防抖函數(shù)時(shí),都會清除之前的定時(shí)器,這樣就保證了函數(shù)不會被立即執(zhí)行,直到等待時(shí)間結(jié)束。

  • setTimeout:在指定的延遲時(shí)間后執(zhí)行傳入的目標(biāo)函數(shù) func,并傳遞原始參數(shù)。

為什么寫成了 ReturnType<typeof setTimeout> | null 這樣的類型 ?

在 JavaScript 中,setTimeout 是一個(gè)內(nèi)置函數(shù),用來設(shè)置一個(gè)延遲執(zhí)行的任務(wù)。它的基本語法如下:

typescript代碼解讀復(fù)制代碼let id = setTimeout(() => {     console.log("Hello, world!"); }, 1000); 

setTimeout 返回一個(gè)定時(shí)器 ID(在瀏覽器中是一個(gè)數(shù)字),這個(gè) ID 用來唯一標(biāo)識這個(gè)定時(shí)器。如果你想取消定時(shí)器,你可以使用 clearTimeout(id),其中 id 就是這個(gè)返回的定時(shí)器 ID。

ReturnType<T> 是 TypeScript 提供的一個(gè)工具類型,它的作用是幫助我們獲取某個(gè)函數(shù)類型的返回值類型。我們通過泛型 T 來傳入一個(gè)函數(shù)類型,然后 ReturnType<T> 就會返回這個(gè)函數(shù)的返回值類型。在這里我們可以用它來獲取 setTimeout 函數(shù)的返回類型。

為什么需要使用 ReturnType<typeof setTimeout> ?

 
由于不同的 JavaScript 運(yùn)行環(huán)境中,setTimeout 的返回值類型是不同的:

  • 瀏覽器中,setTimeout 返回的是一個(gè)數(shù)字 ID

  • Node.js 中,setTimeout 返回的是一個(gè)對象(Timeout 對象)。

為了兼容不同的環(huán)境,我們需要用 ReturnType<typeof setTimeout> 來動態(tài)獲取 setTimeout 返回的類型,而不是手動指定類型(比如 number 或 Timeout)。

typescript代碼解讀復(fù)制代碼let timer: ReturnType<typeof setTimeout>; 

這里 ReturnType<typeof setTimeout> 表示我們根據(jù) setTimeout 的返回值類型自動推導(dǎo)出變量 timer 的類型,不管是數(shù)字(瀏覽器)還是對象(Node.js),TypeScript 會自動處理。

為什么需要設(shè)置聯(lián)合類型 | null ?

在我們的防抖函數(shù)實(shí)現(xiàn)中,定時(shí)器 timer 并不是一開始就設(shè)置好的。我們需要在每次調(diào)用防抖函數(shù)時(shí)動態(tài)設(shè)置定時(shí)器,所以初始狀態(tài)下,timer 的值應(yīng)該是 null

使用 | null 表示聯(lián)合類型,它允許 timer 變量既可以是 setTimeout 返回的值,也可以是 null,表示目前還沒有設(shè)置定時(shí)器。

typescript代碼解讀復(fù)制代碼let timer: ReturnType<typeof setTimeout> | null = null; 
  • ReturnType<typeof setTimeout>:表示 timer 可以是 setTimeout 返回的定時(shí)器 ID。

  • | null:表示在初始狀態(tài)下,timer 沒有定時(shí)器,它的值為 null

第四步:返回一個(gè)新函數(shù)

在防抖函數(shù) debounce 中,我們希望當(dāng)它被調(diào)用時(shí),返回一個(gè)新的函數(shù)。這是防抖函數(shù)的核心機(jī)制,因?yàn)槊看握{(diào)用返回的新函數(shù),實(shí)際上是在控制目標(biāo)函數(shù) func 的執(zhí)行。

具體的想法是這樣的:我們并不直接執(zhí)行傳入的目標(biāo)函數(shù) func,而是返回一個(gè)新函數(shù),這個(gè)新函數(shù)在被調(diào)用時(shí)會受到防抖的控制。

因此,我們要修改 debounce 函數(shù),使它返回一個(gè)新的函數(shù),真正控制 func 的執(zhí)行時(shí)機(jī)。

typescript代碼解讀復(fù)制代碼function debounce(func: Function, duration: number) {     let timer: ReturnType<typeof setTimeout> | null = null; // 定時(shí)器變量      return function () {         // 防抖邏輯將在這里編寫     }; } 
  • 返回新函數(shù):當(dāng) debounce 被調(diào)用時(shí),它返回一個(gè)新函數(shù)。這個(gè)新函數(shù)是每次調(diào)用時(shí)執(zhí)行防抖邏輯的入口。

  • 為什么返回新函數(shù)? :因?yàn)槲覀冃枰诿看问录|發(fā)時(shí)(例如用戶輸入時(shí))執(zhí)行防抖操作,而不是直接執(zhí)行傳入的目標(biāo)函數(shù) func

第五步:清除之前的定時(shí)器

為了實(shí)現(xiàn)防抖功能,每次調(diào)用返回的新函數(shù)時(shí),我們需要先清除之前的定時(shí)器。如果之前有一個(gè)定時(shí)器在等待執(zhí)行目標(biāo)函數(shù),我們應(yīng)該將其取消,然后重新設(shè)置一個(gè)新的定時(shí)器。

這個(gè)步驟的關(guān)鍵就是使用 clearTimeout(timer) 。

typescript代碼解讀復(fù)制代碼function debounce(func: Function, duration: number) {     let timer: ReturnType<typeof setTimeout> | null = null; // 定時(shí)器變量      return function () {         if (timer) {             clearTimeout(timer);  // 清除之前的定時(shí)器         }          // 下面將設(shè)置新的定時(shí)器     }; } 
  • if (timer) :我們檢查 timer 是否有值。如果它有值,說明之前的定時(shí)器還在等待執(zhí)行,我們需要將其清除。

  • clearTimeout(timer) :這就是清除之前的定時(shí)器,防止之前的調(diào)用被執(zhí)行。這個(gè)操作非常關(guān)鍵,因?yàn)樗_保了只有最后一次調(diào)用(在延遲時(shí)間后)才會真正觸發(fā)目標(biāo)函數(shù)。

第六步:設(shè)置新的定時(shí)器

現(xiàn)在我們需要在每次調(diào)用返回的新函數(shù)時(shí),重新設(shè)置一個(gè)新的定時(shí)器,讓它在指定的延遲時(shí)間 duration 之后執(zhí)行目標(biāo)函數(shù) func

這時(shí)候就要使用 setTimeout 來設(shè)置定時(shí)器,并在延遲時(shí)間后執(zhí)行目標(biāo)函數(shù)。

typescript代碼解讀復(fù)制代碼function debounce(func: Function, duration: number) {     let timer: ReturnType<typeof setTimeout> | null = null; // 定時(shí)器變量      return function () {         if (timer) {             clearTimeout(timer);  // 清除之前的定時(shí)器         }          timer = setTimeout(() => {             func();  // 延遲后調(diào)用目標(biāo)函數(shù)         }, duration);     }; } 
  • setTimeout:我們使用 setTimeout 來設(shè)置一個(gè)新的定時(shí)器,定時(shí)器將在 duration 毫秒后執(zhí)行傳入的目標(biāo)函數(shù) func

  • func() :這是目標(biāo)函數(shù)的實(shí)際執(zhí)行點(diǎn)。定時(shí)器到達(dá)延遲時(shí)間時(shí),它會執(zhí)行目標(biāo)函數(shù) func

  • timer = setTimeout(...) :我們將定時(shí)器的 ID 存儲在 timer 變量中,以便后續(xù)可以使用 clearTimeout(timer) 來清除定時(shí)器。

第七步:支持參數(shù)傳遞

接下來是讓這個(gè)防抖函數(shù)能夠接受參數(shù),并將這些參數(shù)傳遞給目標(biāo)函數(shù) func

為了實(shí)現(xiàn)這個(gè)功能,我們需要用到 ...args 來捕獲所有傳入的參數(shù),并在執(zhí)行目標(biāo)函數(shù)時(shí)將這些參數(shù)傳遞過去。

typescript代碼解讀復(fù)制代碼function debounce(func: Function, duration: number) {     let timer: ReturnType<typeof setTimeout> | null = null; // 定時(shí)器變量      return function (...args: any[]) {  // 接收傳入的參數(shù)         if (timer) {             clearTimeout(timer);  // 清除之前的定時(shí)器         }          timer = setTimeout(() => {             func(...args);  // 延遲后調(diào)用目標(biāo)函數(shù),并傳遞參數(shù)         }, duration);     }; } 
  • ...args: any[] :這表示新函數(shù)可以接收任意數(shù)量的參數(shù),并將這些參數(shù)存儲在 args 數(shù)組中。

  • func(...args) :當(dāng)定時(shí)器到達(dá)延遲時(shí)間后,調(diào)用目標(biāo)函數(shù) func,并將 args 中的所有參數(shù)傳遞給它。這確保了目標(biāo)函數(shù)能接收到我們傳入的所有參數(shù)。

到這里,我們一個(gè)基本的防抖函數(shù)的實(shí)現(xiàn)。這個(gè)防抖函數(shù)實(shí)現(xiàn)了以下基本功能:

  1. 函數(shù)執(zhí)行的延遲控制:每次調(diào)用時(shí),都重新設(shè)置定時(shí)器,確保函數(shù)不會立即執(zhí)行,而是在延遲結(jié)束后才執(zhí)行。

  2. 多參數(shù)支持:通過 ...args,防抖函數(shù)能夠接收多個(gè)參數(shù),并將它們傳遞給目標(biāo)函數(shù)。

  3. 清除之前的定時(shí)器:在每次調(diào)用時(shí),如果定時(shí)器已經(jīng)存在,先清除之前的定時(shí)器,確保只有最后一次調(diào)用才會生效。

但是,這樣就完了嗎?

在當(dāng)前的實(shí)現(xiàn)中,debounce 函數(shù)的定義是 debounce(func: Function, duration: number),其中 func: Function 用來表示目標(biāo)函數(shù)。這種定義雖然可以工作,但它存在明顯的缺陷和不足之處,尤其是在 TypeScript 強(qiáng)調(diào)類型安全的情況下。

缺陷 1:缺乏參數(shù)類型檢查

Function 是一種非常寬泛的類型,它允許目標(biāo)函數(shù)接收任何類型、任意數(shù)量的參數(shù)。因此定義目標(biāo)函數(shù) func 為 Function 類型意味著 TypeScript 無法對目標(biāo)函數(shù)的參數(shù)類型進(jìn)行任何檢查。

typescript代碼解讀復(fù)制代碼const debounced = debounce((a: number, b: number) => {     console.log(a + b); }, 200);  debounced("hello", "world");  // 這里不會報(bào)錯(cuò),參數(shù)類型不匹配,但仍會被調(diào)用 

在這個(gè)例子中,我們定義了一個(gè)目標(biāo)函數(shù),期望它接受兩個(gè)數(shù)字類型的參數(shù),但在實(shí)際調(diào)用時(shí)卻傳入了兩個(gè)字符串。

這種情況下 TypeScript 不會提示任何錯(cuò)誤,因?yàn)?nbsp;Function 類型沒有對參數(shù)類型進(jìn)行限制。這種類型檢查的缺失可能導(dǎo)致運(yùn)行時(shí)錯(cuò)誤或者邏輯上的錯(cuò)誤。

缺陷 2:返回值類型不安全

同樣,定義 func 為 Function 類型時(shí),TypeScript 無法推斷目標(biāo)函數(shù)的返回值類型。這意味著防抖函數(shù)不能保證目標(biāo)函數(shù)的返回值是符合預(yù)期的類型,可能導(dǎo)致返回值在其他地方被錯(cuò)誤使用。

typescript代碼解讀復(fù)制代碼const debounced = debounce(() => {     return "result"; }, 200);  const result = debounced();  // TypeScript 不知道返回值類型,認(rèn)為是 undefined 



在這個(gè)例子中,雖然目標(biāo)函數(shù)明確返回了一個(gè)字符串 "result",但 debounced 函數(shù)的返回值類型未被推斷出來,因此 TypeScript 會認(rèn)為它的返回值是 void 或 undefined,即使目標(biāo)函數(shù)實(shí)際上返回了 string

缺陷 3:缺乏目標(biāo)函數(shù)的簽名限制

由于 Function 類型允許任何形式的函數(shù),因此 TypeScript 也無法檢查目標(biāo)函數(shù)的參數(shù)個(gè)數(shù)和類型是否匹配。這種情況下,如果防抖函數(shù)返回的新函數(shù)接收了錯(cuò)誤數(shù)量或類型的參數(shù),可能導(dǎo)致函數(shù)行為異常或意外的運(yùn)行時(shí)錯(cuò)誤。

typescript代碼解讀復(fù)制代碼const debounced = debounce((a: number) => {     console.log(a); }, 200);  debounced(1, 2, 3);  // TypeScript 不會報(bào)錯(cuò),但多余的參數(shù)不會被使用 

雖然目標(biāo)函數(shù)只期望接收一個(gè)參數(shù),但在調(diào)用時(shí)傳入了多個(gè)參數(shù)。TypeScript 不會進(jìn)行任何警告或報(bào)錯(cuò),因?yàn)?nbsp;Function 類型允許這種寬泛的調(diào)用,這可能會導(dǎo)致開發(fā)者誤以為這些參數(shù)被使用。

總結(jié) func: Function 的缺陷

  • 缺乏參數(shù)類型檢查任何數(shù)量、任意類型的參數(shù)都可以傳遞給目標(biāo)函數(shù),導(dǎo)致潛在的參數(shù)類型錯(cuò)誤。

  • 返回值類型不安全目標(biāo)函數(shù)的返回值類型無法被推斷,導(dǎo)致 TypeScript 無法確保返回值的類型正確。

  • 函數(shù)簽名不受限制沒有對目標(biāo)函數(shù)的參數(shù)個(gè)數(shù)和類型進(jìn)行檢查,容易導(dǎo)致邏輯錯(cuò)誤或參數(shù)使用不當(dāng)。

這些缺陷使得代碼在類型安全性和健壯性上存在不足,可能導(dǎo)致運(yùn)行時(shí)錯(cuò)誤或者隱藏的邏輯漏洞。

下一步的改進(jìn)

為了解決這些缺陷,我們可以通過泛型的方式為目標(biāo)函數(shù)添加類型限制,確保目標(biāo)函數(shù)的參數(shù)和返回值類型都能被準(zhǔn)確地推斷和檢查。這會是我們接下來要進(jìn)行的優(yōu)化。

第八步:使用泛型優(yōu)化

為了克服 func: Function 帶來的缺陷,我們可以通過 泛型 來優(yōu)化防抖函數(shù)的類型定義,確保目標(biāo)函數(shù)的參數(shù)和返回值都能在編譯時(shí)進(jìn)行類型檢查。使用泛型不僅可以解決參數(shù)類型和返回值類型的檢查問題,還可以提升代碼的靈活性和安全性。

如何使用泛型進(jìn)行優(yōu)化?

我們將通過引入兩個(gè)泛型參數(shù)來改進(jìn)防抖函數(shù)的類型定義:

  1. A:表示目標(biāo)函數(shù)的參數(shù)類型,可以是任意類型和數(shù)量的參數(shù),確保防抖函數(shù)在接收參數(shù)時(shí)能進(jìn)行類型檢查。

  2. R:表示目標(biāo)函數(shù)的返回值類型,確保防抖函數(shù)返回的值與目標(biāo)函數(shù)一致。

typescript代碼解讀復(fù)制代碼function debounce<A extends any[], R>(     func: (...args: A) => R,  // 使用泛型 A 表示參數(shù),R 表示返回值類型     duration: number          // 延遲時(shí)間,以毫秒為單位 ): (...args: A) => R {  // 返回新函數(shù),參數(shù)類型與目標(biāo)函數(shù)相同,返回值類型為 R     let timer: ReturnType<typeof setTimeout> | null = null;  // 定時(shí)器變量     let lastResult: R;  // 存儲目標(biāo)函數(shù)的返回值      return function (...args: A): R {  // 返回的新函數(shù),參數(shù)類型由 A 推斷         if (timer) {             clearTimeout(timer);  // 清除之前的定時(shí)器         }          timer = setTimeout(() => {             lastResult = func(...args);  // 延遲后調(diào)用目標(biāo)函數(shù),并存儲返回值         }, duration);          return lastResult;  // 返回上一次執(zhí)行的結(jié)果,如果尚未執(zhí)行則返回 undefined     }; } 
  1. A extends any[] :A 表示目標(biāo)函數(shù)的參數(shù)類型,A 是一個(gè)數(shù)組類型,能夠適應(yīng)目標(biāo)函數(shù)接收多個(gè)參數(shù)的場景。通過泛型,防抖函數(shù)能夠根據(jù)目標(biāo)函數(shù)的簽名推斷出參數(shù)類型并進(jìn)行檢查。

  2. RR 表示目標(biāo)函數(shù)的返回值類型,防抖函數(shù)能夠確保返回值類型與目標(biāo)函數(shù)一致。如果目標(biāo)函數(shù)返回值類型為 string,防抖函數(shù)也會返回 string,這樣可以防止返回值類型不匹配。

  3. lastResult:用來存儲目標(biāo)函數(shù)的最后一次返回值。每次調(diào)用目標(biāo)函數(shù)時(shí)會更新 lastResult,并在調(diào)用時(shí)返回上一次執(zhí)行的結(jié)果,確保防抖函數(shù)返回正確的返回值。

泛型優(yōu)化后的優(yōu)點(diǎn):

  1. 類型安全的參數(shù)傳遞
    通過泛型 A,防抖函數(shù)可以根據(jù)目標(biāo)函數(shù)的簽名進(jìn)行類型檢查,確保傳入的參數(shù)與目標(biāo)函數(shù)一致,避免參數(shù)類型錯(cuò)誤。

    typescript代碼解讀復(fù)制代碼const debounced1 = debounce((a: number, b: string) => {     console.log(a, b); }, 300);  debounced1(42, "hello");  // 正確,參數(shù)類型匹配 debounced1("42", 42);     // 錯(cuò)誤,類型不匹配 

  2. 返回值類型安全
    泛型 R 確保了防抖函數(shù)的返回值與目標(biāo)函數(shù)的返回值類型一致,防止不匹配的類型被返回。

    typescript代碼解讀復(fù)制代碼const debounced = debounce(() => {     return "result"; }, 200);  const result = debounced();  // 返回值為 string console.log(result);         // 輸出 "result" 

  3. 支持多參數(shù)傳遞
    泛型 A 表示參數(shù)類型數(shù)組,這意味著目標(biāo)函數(shù)可以接收多個(gè)參數(shù),防抖函數(shù)會將這些參數(shù)正確傳遞給目標(biāo)函數(shù)。而如果防抖函數(shù)返回的新函數(shù)接收了錯(cuò)誤數(shù)量或類型的參數(shù),會直接報(bào)錯(cuò)提示。

    typescript代碼解讀復(fù)制代碼const debounced = debounce((name: string, age: number) => {     return `${name} is ${age} years old.`; }, 300);  const result = debounced("Alice", 30); console.log(result);  // 輸出 "Alice is 30 years old." 


第九步:添加 cancel 方法并處理返回值類型

在前面的步驟中,我們已經(jīng)實(shí)現(xiàn)了一個(gè)可以延遲執(zhí)行的防抖函數(shù),并且支持參數(shù)傳遞和返回目標(biāo)函數(shù)的結(jié)果。

但是,由于防抖函數(shù)的執(zhí)行是異步延遲的,因此在初次調(diào)用時(shí),防抖函數(shù)可能無法立即返回結(jié)果。因此函數(shù)的返回值我們需要使用 undefined 來表示目標(biāo)函數(shù)的返回結(jié)果可能出現(xiàn)還沒生成的情況。

除此之外,我們還要為防抖函數(shù)添加一個(gè) cancel 方法,用于手動取消防抖的延遲執(zhí)行。

為什么需要 cancel 方法?

在一些場景下,可能需要手動取消防抖操作,例如:

  • 用戶取消了操作,不希望目標(biāo)函數(shù)再執(zhí)行。

  • 某個(gè)事件或操作已經(jīng)不再需要處理,因此需要取消延遲中的函數(shù)調(diào)用。

為了解決這些需求,cancel 方法可以幫助我們在定時(shí)器還未觸發(fā)時(shí),清除定時(shí)器并停止目標(biāo)函數(shù)的執(zhí)行。

typescript代碼解讀復(fù)制代碼// 定義帶有 cancel 方法的防抖函數(shù)類型 type DebouncedFunction<A extends any[], R> = {     (...args: A): R | undefined; // 防抖函數(shù)本身,返回值可能為 R 或 undefined     cancel: () => void;          // `cancel` 方法,用于手動清除防抖 };  // 實(shí)現(xiàn)防抖函數(shù) function debounce<A extends any[], R>(     func: (...args: A) => R,  // 泛型 A 表示參數(shù)類型,R 表示返回值類型     duration: number          // 延遲時(shí)間 ): DebouncedFunction<A, R> {  // 返回帶有 cancel 方法的防抖函數(shù)     let timer: ReturnType<typeof setTimeout> | null = null;  // 定時(shí)器變量     let lastResult: R | undefined;  // 用于存儲目標(biāo)函數(shù)的返回值      // 防抖邏輯的核心函數(shù)     const debouncedFn = function (...args: A): R | undefined {         if (timer) {             clearTimeout(timer);  // 清除之前的定時(shí)器         }          // 設(shè)置新的定時(shí)器         timer = setTimeout(() => {             lastResult = func(...args);  // 延遲后執(zhí)行目標(biāo)函數(shù),并存儲返回值         }, duration);          // 返回上一次的結(jié)果或 undefined         return lastResult;     };      // 添加 `cancel` 方法,用于手動取消防抖     debouncedFn.cancel = function () {         if (timer) {             clearTimeout(timer);  // 清除定時(shí)器             timer = null;         // 重置定時(shí)器         }     };      return debouncedFn;  // 返回帶有 `cancel` 方法的防抖函數(shù) } 
  1. 返回值類型 R | undefined

    • R:代表目標(biāo)函數(shù)的返回值類型,例如 number 或 string

    • undefined:在防抖函數(shù)的首次調(diào)用或目標(biāo)函數(shù)尚未執(zhí)行時(shí),返回 undefined,表示結(jié)果尚未生成。

    • lastResult 用于存儲目標(biāo)函數(shù)上一次執(zhí)行的結(jié)果,防抖函數(shù)在每次調(diào)用時(shí)會返回該結(jié)果,或者在尚未執(zhí)行時(shí)返回 undefined

  2. cancel 方法

    • cancel 方法的作用是清除當(dāng)前的定時(shí)器,防止目標(biāo)函數(shù)在延遲時(shí)間結(jié)束后被執(zhí)行。

    • 通過調(diào)用 clearTimeout(timer),我們可以停止掛起的防抖操作,并將 timer 重置為 null,表示當(dāng)前沒有掛起的定時(shí)器。

讓我們來看一個(gè)具體的使用示例,展示如何使用防抖函數(shù),并在需要時(shí)手動取消操作。

typescript代碼解讀復(fù)制代碼// 定義一個(gè)簡單的目標(biāo)函數(shù) const debouncedLog = debounce((message: string) => {     console.log(message);     return message; }, 300);  // 第一次調(diào)用防抖函數(shù),目標(biāo)函數(shù)將在 300 毫秒后執(zhí)行 debouncedLog("Hello");  // 如果不取消,300ms 后會輸出 "Hello"  // 手動取消防抖,目標(biāo)函數(shù)不會執(zhí)行 debouncedLog.cancel(); 

在這個(gè)示例中:

  1. 調(diào)用 debouncedLog("Hello") :會啟動一個(gè) 300 毫秒的延遲執(zhí)行,目標(biāo)函數(shù)計(jì)劃在 300 毫秒后執(zhí)行,并輸出 "Hello"

  2. 調(diào)用 debouncedLog.cancel() :會清除定時(shí)器,目標(biāo)函數(shù)不會執(zhí)行,避免了不必要的操作。

第十步:將防抖函數(shù)作為工具函數(shù)單獨(dú)放在一個(gè) ts 文件中并添加 JSDoc 注釋

在編寫好防抖函數(shù)之后,下一步是將其作為一個(gè)工具函數(shù)放入單獨(dú)的 .ts 文件中,以便在項(xiàng)目中重復(fù)使用。同時(shí),我們可以為函數(shù)添加詳細(xì)的 JSDoc 注釋,方便使用者了解函數(shù)的作用、參數(shù)、返回值及用法。

1. 將防抖函數(shù)放入單獨(dú)的文件

首先,我們可以創(chuàng)建一個(gè)名為 debounce.ts 的文件,并將防抖函數(shù)的代碼放在其中。

typescript代碼解讀復(fù)制代碼// debounce.ts  export type DebouncedFunction<A extends any[], R> = {     (...args: A): R | undefined;  // 防抖函數(shù)本身,返回值可能為 R 或 undefined     cancel: () => void;           // `cancel` 方法,用于手動清除防抖 };  /**  * 創(chuàng)建一個(gè)防抖函數(shù),確保在最后一次調(diào)用后,目標(biāo)函數(shù)只會在指定的延遲時(shí)間后執(zhí)行。  * 防抖函數(shù)可以防止某個(gè)函數(shù)被頻繁調(diào)用,例如用戶輸入事件、滾動事件或窗口調(diào)整大小等場景。  *   * @template A - 函數(shù)接受的參數(shù)類型。  * @template R - 函數(shù)的返回值類型。  * @param {(...args: A) => R} func - 需要防抖的目標(biāo)函數(shù)。該函數(shù)將在延遲時(shí)間后執(zhí)行。  * @param {number} duration - 延遲時(shí)間(以毫秒為單位)。在這個(gè)時(shí)間內(nèi),如果再次調(diào)用函數(shù),將重新計(jì)時(shí)。  * @returns {DebouncedFunction<A, R>} 一個(gè)防抖后的函數(shù),該函數(shù)包括一個(gè) `cancel` 方法用于清除防抖。  *   * @example  * const debouncedLog = debounce((message: string) => {  *   console.log(message);  *   return message;  * }, 300);  *   * debouncedLog("Hello");  // 300ms 后輸出 "Hello"  * debouncedLog.cancel();  // 取消防抖,函數(shù)不會執(zhí)行  */ export function debounce<A extends any[], R>(     func: (...args: A) => R,     duration: number ): DebouncedFunction<A, R> {     let timer: ReturnType<typeof setTimeout> | null = null;  // 定時(shí)器變量     let lastResult: R | undefined;  // 存儲目標(biāo)函數(shù)的返回值      const debouncedFn = function (...args: A): R | undefined {         if (timer) {             clearTimeout(timer);  // 清除之前的定時(shí)器         }          timer = setTimeout(() => {             lastResult = func(...args);  // 延遲后執(zhí)行目標(biāo)函數(shù),并存儲返回值         }, duration);          return lastResult;  // 返回上次執(zhí)行的結(jié)果,如果尚未執(zhí)行則返回 undefined     };      debouncedFn.cancel = function () {         if (timer) {             clearTimeout(timer);  // 清除定時(shí)器,防止目標(biāo)函數(shù)被執(zhí)行             timer = null;         // 重置定時(shí)器         }     };      return debouncedFn; } 

2. 詳細(xì)的 JSDoc 注釋說明

通過添加 JSDoc 注釋,能夠?yàn)楹瘮?shù)使用者提供清晰的文檔信息,說明防抖函數(shù)的功能、參數(shù)類型、返回值類型,以及如何使用它。

JSDoc 注釋的結(jié)構(gòu)說明

  1. @template A, R:說明泛型 A 是函數(shù)接受的參數(shù)類型,R 是目標(biāo)函數(shù)的返回值類型。

  2. @param:解釋函數(shù)的輸入?yún)?shù),說明 func 是目標(biāo)函數(shù),duration 是防抖的延遲時(shí)間。

  3. @returns:說明返回值是一個(gè)帶有 cancel 方法的防抖函數(shù),函數(shù)返回值類型是 R | undefined

  4. @example:為函數(shù)提供示例,展示防抖函數(shù)的典型用法,包括取消防抖操作。

使用 JSDoc 生成文檔

通過在 .ts 文件中添加 JSDoc 注釋,可以借助 TypeScript 編輯器或 IDE(如 VSCode/Webstorm)自動生成代碼提示和函數(shù)文檔說明,提升開發(fā)體驗(yàn)。
例如,當(dāng)開發(fā)者在使用 debounce 函數(shù)時(shí),可以自動看到函數(shù)的說明和參數(shù)類型提示:

回顧:泛型防抖函數(shù)的最終效果

通過前面各個(gè)步驟的優(yōu)化,我們已經(jīng)構(gòu)建了一個(gè)類型安全的防抖函數(shù),結(jié)合泛型實(shí)現(xiàn)了以下關(guān)鍵功能:

  1. 類型安全的參數(shù)傳遞
    通過泛型 A,防抖函數(shù)能夠根據(jù)目標(biāo)函數(shù)的簽名進(jìn)行參數(shù)類型檢查,確保傳入的參數(shù)與目標(biāo)函數(shù)的類型一致。如果傳入的參數(shù)類型不匹配,TypeScript 將在編譯時(shí)報(bào)錯(cuò),避免運(yùn)行時(shí)的潛在錯(cuò)誤。

    typescript代碼解讀復(fù)制代碼const debounced1 = debounce((a: number, b: string) => {     console.log(a, b); }, 300);  debounced1(42, "hello");  // 正確,參數(shù)類型匹配 debounced1("42", 42);     // 錯(cuò)誤,類型不匹配 

    在上面的例子中,TypeScript 會檢查參數(shù)類型,確保傳入的參數(shù)符合預(yù)期的類型。錯(cuò)誤的參數(shù)類型會被及時(shí)捕捉。

  2. 返回值類型安全
    泛型 R 確保防抖函數(shù)的返回值與目標(biāo)函數(shù)的返回值類型保持一致。TypeScript 可以根據(jù)目標(biāo)函數(shù)的返回值類型推斷防抖函數(shù)的返回值,防止不匹配的類型被返回。

    typescript代碼解讀復(fù)制代碼const debounced = debounce(() => {     return "result"; }, 200);  const result = debounced();  // 返回值為 string console.log(result);         // 輸出 "result" 



    在這個(gè)例子中,debounce 返回的防抖函數(shù)的返回值類型為 string 或者 undefind ,因?yàn)樵诜蓝逗瘮?shù)的實(shí)現(xiàn)中,目標(biāo)函數(shù)是延遲執(zhí)行的,因此在初次調(diào)用或在延遲期間debounced 函數(shù)返回的結(jié)果可能尚未生成,與目標(biāo)函數(shù)的返回值類型預(yù)期一致。

  3. 支持多參數(shù)傳遞
    泛型 A 表示目標(biāo)函數(shù)的參數(shù)類型數(shù)組,這意味著防抖函數(shù)可以正確傳遞多個(gè)參數(shù),并確保類型安全。如果傳入了錯(cuò)誤數(shù)量或類型的參數(shù),TypeScript 會提示開發(fā)者進(jìn)行修正。

    typescript代碼解讀復(fù)制代碼const debounced = debounce((name: string, age: number) => {return `${name} is ${age} years old.`; }, 300);const result = debounced("Alice", 30);console.log(result);  // 輸出 "Alice is 30 years old." 

    在這個(gè)例子中,防抖函數(shù)正確地將多個(gè)參數(shù)傳遞給目標(biāo)函數(shù),并輸出目標(biāo)函數(shù)的正確返回值。傳入的參數(shù)數(shù)量或類型不正確時(shí),TypeScript 會發(fā)出報(bào)錯(cuò)提示。


總結(jié)

至此,我們完整實(shí)現(xiàn)并優(yōu)化了一個(gè)類型安全的防抖函數(shù),并通過泛型確保參數(shù)和返回值的類型安全。此外,我們還詳細(xì)講解了如何為防抖函數(shù)添加 cancel 方法,并處理延遲執(zhí)行的返回值 R | undefined。最后,我們將防抖函數(shù)封裝在一個(gè)單獨(dú)的 TypeScript 文件中,并為其添加了 JSDoc 注釋,使其成為一個(gè)可復(fù)用的工具函數(shù)。

通過這種方式,防抖函數(shù)不僅功能強(qiáng)大,還能在編譯時(shí)提供類型檢查,減少運(yùn)行時(shí)的潛在錯(cuò)誤。TypeScript 的類型系統(tǒng)幫助我們提升了代碼的安全性和健壯性。
最后,我們給出完整的的代碼如下:

// debounce.ts


export type DebouncedFunction<A extends any[], R> = {

    (...args: A): R | undefined;  // 防抖函數(shù)本身,返回值可能為 R 或 undefined

    cancel: () => void;           // `cancel` 方法,用于手動清除防抖

};


/**

 * 創(chuàng)建一個(gè)防抖函數(shù),確保在最后一次調(diào)用后,目標(biāo)函數(shù)只會在指定的延遲時(shí)間后執(zhí)行。

 * 防抖函數(shù)可以防止某個(gè)函數(shù)被頻繁調(diào)用,例如用戶輸入事件、滾動事件或窗口調(diào)整大小等場景。

 * 

 * @template A - 函數(shù)接受的參數(shù)類型。

 * @template R - 函數(shù)的返回值類型。

 * @param {(...args: A) => R} func - 需要防抖的目標(biāo)函數(shù)。該函數(shù)將在延遲時(shí)間后執(zhí)行。

 * @param {number} duration - 延遲時(shí)間(以毫秒為單位)。在這個(gè)時(shí)間內(nèi),如果再次調(diào)用函數(shù),將重新計(jì)時(shí)。

 * @returns {DebouncedFunction<A, R>} 一個(gè)防抖后的函數(shù),該函數(shù)包括一個(gè) `cancel` 方法用于清除防抖。

 * 

 * @example

 * const debouncedLog = debounce((message: string) => {

 *   console.log(message);

 *   return message;

 * }, 300);

 * 

 * debouncedLog("Hello");  // 300ms 后輸出 "Hello"

 * debouncedLog.cancel();  // 取消防抖,函數(shù)不會執(zhí)行

 */

export function debounce<A extends any[], R>(

    func: (...args: A) => R,

    duration: number

): DebouncedFunction<A, R> {

    let timer: ReturnType<typeof setTimeout> | null = null;  // 定時(shí)器變量

    let lastResult: R | undefined;  // 存儲目標(biāo)函數(shù)的返回值


    const debouncedFn = function (...args: A): R | undefined {

        if (timer) {

            clearTimeout(timer);  // 清除之前的定時(shí)器

        }


        timer = setTimeout(() => {

            lastResult = func(...args);  // 延遲后執(zhí)行目標(biāo)函數(shù),并存儲返回值

        }, duration);


        return lastResult;  // 返回上次執(zhí)行的結(jié)果,如果尚未執(zhí)行則返回 undefined

    };


    debouncedFn.cancel = function () {

        if (timer) {

            clearTimeout(timer);  // 清除定時(shí)器,防止目標(biāo)函數(shù)被執(zhí)行

            timer = null;         // 重置定時(shí)器

        }

    };


    return debouncedFn;

}


該文章在 2024/11/18 17:52:59 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved