首頁 > 軟體

vscode工具函數once使用範例深入剖析

2023-03-29 06:01:41

背景

once 函數的起源可以追溯到函數語言程式設計。在函數語言程式設計中,函數被視為不可變的,這意味著它們不應修改任何狀態或參照任何狀態。因此,在函數語言程式設計中,通常需要一些技巧來處理一些常見問題,例如避免在多次呼叫相同函數時進行冗餘計算。

once 函數是解決此問題的一種方法。它可以將函數轉換為只能呼叫一次的函數,並使用一些技巧來避免冗餘計算。使用 once 函數可以減少計算時間和資源的消耗,並提高應用程式的效能。

once 函數在現代 JavaScript 庫和框架中非常常見,例如 lodash。它們提供了許多內建的 once 函數來處理各種場景和問題。在實際開發中,我們可以使用這些函數來提高程式碼的可維護性和可讀性,避免冗餘計算和網路請求,並提高應用程式的效能。

VSCode中的實現

export function once<T extends Function>(this: unknown, fn: T): T {
	const _this = this;
	let didCall = false;
	let result: unknown;
	return function () {
		if (didCall) {
			return result;
		}
		didCall = true;
		result = fn.apply(_this, arguments);
		return result;
	} as unknown as T;
}

這個實現比較簡單,但還是詳細分析解釋一下程式碼

  • 函數簽名:once<T extends Function>(this: unknown, fn: T): T。該函數使用了泛型引數**T,表示被封裝的原始函數的型別。它還定義了一個this引數,表示封裝函數中的this關鍵字的型別為unknown,以及一個fn**引數,表示被封裝的原始函數。
  • 快取結果:在函數內部,定義了三個變數。**_this變數參照了函數中的this關鍵字,並將其儲存在變數中。didCall變數用於跟蹤函數是否已經被呼叫過。result**變數用於儲存函數的結果。
  • 返回新函數:函數內部返回了一個匿名函數,它接受任意數量的引數,並將其傳遞給原始函數。在函數內部,首先檢查函數是否已經被呼叫過。如果是,則直接返回之前儲存的結果。否則,將**didCall**標記為已呼叫,並呼叫原始函數並儲存結果。最後,返回結果。
  • 型別斷言:函數的返回值是一個匿名函數,但是它的型別需要與原始函數相同。因此,使用了一個型別斷言將匿名函數的型別強制轉換為泛型引數**T**。

lodash的實現

lodash 到目前的版本已經是高度封裝了,它的原始碼可以在**github.com/lodash/loda…**看到:

function once(func) {
  return before(2, func)
}

它本身是呼叫了 before 函數, before 是一個經過高度抽象的函數,它在被呼叫次數達到指定次數之前,會繼續執行傳入的函數,而在達到指定次數時,會返回最後一次執行傳入函數的結果:

function before(n, func) {
  let result
  if (typeof func !== 'function') {
    throw new TypeError('Expected a function')
  }
  return function(...args) {
    if (--n > 0) {
      result = func.apply(this, args)
    }
    if (n <= 1) {
      func = undefined
    }
    return result
  }
}

對於 lodash 這樣的工具庫而言,它的抽象層次要比 vscode 更高,但實現的原理是相同的,核心都是利用閉包,通過內部變數的狀態來判斷函數是否已經被呼叫過,從而保證原始函數只被執行一次。

once的應用

**once**函數是一個常見的JavaScript函數,它用於確保一個函數只能被呼叫一次。這個函數在以下幾個場景下非常有用:

  • 快取函數的結果:當一個函數需要執行復雜的計算時,我們可以使用**once**函數來確保它只會被呼叫一次,並將結果快取下來。在之後的呼叫中,函數會直接返回快取的結果,而不會再次執行計算。這可以節省計算時間和資源,並提高應用程式的效能。
  • 避免重複網路請求:當我們需要向伺服器傳送請求時,可以使用**once**函數來確保只傳送一次請求,並在之後的呼叫中直接返回結果。這可以避免傳送重複的網路請求,並提高應用程式的響應速度。
  • 確保只執行一次的初始化程式碼:有時候我們需要在應用程式啟動時執行一些初始化程式碼,例如建立全域性變數或註冊事件處理程式。在這種情況下,可以使用**once**函數來確保這些程式碼只會被執行一次,避免不必要的重複操作。

在VSCode中, once 用的非常頻繁,通過檢視 reference 我們可以看到非常多的使用:

once的注意事項

this指標問題

once 函數內部,定義了一個名為 _this 的變數,它儲存了函數的 this 關鍵字。這是因為,在使用 applycall 方法呼叫函數時,需要確保函數中的 this 關鍵字被正確地繫結。如果沒有儲存 _this 變數,而是直接使用 this 關鍵字,可能會導致 this 關鍵字在多次呼叫中被意外地修改,從而導致錯誤或異常。

在使用 once 函數時,如果原始函數需要使用 this 關鍵字,需要確保 this 關鍵字被正確地繫結。看看以下程式碼:

class Counter {
  private count = 0;
  constructor(private readonly name: string) {}
  increment() {
    console.log(`${this.name}: Count = ${++this.count}`);
  }
}
const counter1 = new Counter('Counter 1');
const counter2 = new Counter('Counter 2');
const incrementOnce = once(counter1.increment);
incrementOnce.call(counter1); // 輸出 "Counter 1: Count = 1"
incrementOnce.call(counter2); // 輸出 "Counter 1: Count = 2",而不是 "Counter 2: Count = 1"
incrementOnce.call(counter1); // 輸出 "Counter 1: Count = 2"

在上述程式碼中,如果我們在多次呼叫 incrementOnce 函數時,使用了不同的 this 關鍵字,可能會導致輸出結果不正確。例如,上述程式碼中的第二次呼叫 incrementOnce 函數時,使用了 counter2 作為 this 關鍵字,而實際上 incrementOnce 函數中儲存的 this 關鍵字是 counter1,因此輸出結果為 "Counter 1: Count = 2",而不是 "Counter 2: Count = 1"。

因此,在使用 once 函數時,需要確保原始函數能夠正確地處理 this 關鍵字,並且在呼叫 once 函數時,應該指定正確的 this 關鍵字,以避免意外的錯誤或異常。

小結

本文對 once 函數進行了詳細的剖析,包括其背景、實現原理和應用場景。在實際開發中,once 函數可以用於快取函數的結果、避免重複網路請求以及確保只執行一次的初始化程式碼。在使用 once 函數時,需要注意原始函數的 this 關鍵字的繫結,以避免意外的錯誤或異常。

在我們的日常開發中,見過太多的程式碼為了保證只執行一次,在本地定一個類似 flag 的變數來控制,這種寫法非常不優雅,在這種場景下,使用 once 函數是一個更好的選擇。

以上就是vscode工具函數once使用範例深入剖析的詳細內容,更多關於vscode工具函數once的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com