Thinking系列,旨在利用10分鐘的時間傳達一種可落地的編程思想,
參考某第三方模塊某函式:
// 將一些數字轉換為可讀的字串
import { toReadableNumber } from 'some-library'
const readableNumbers = someNumbers.map(toReadableNumber)
toReadableNumber 的實作:
export function toReadableNumber(num) {
// 例如 10000000 轉換成 '10,000,000'
}
上述的示例運行良好,直到 some-library 更新,但并不是 some-library 庫導致的 – 因為其從未將 toReadableNumber 設計為 array.map 的回呼,
// 我們認為:
const readableNumbers = someNumbers.map(toReadableNumber)
// 或者是:
const readableNumbers = someNumbers.map((n) => toReadableNumber(n))
// 更甚至:
const readableNumbers = someNumbers.map((item, index, arr) =>
toReadableNumber(item, index, arr)
)
我們將陣列中專案的索引和陣列本身傳遞給 toReadableNumber,起初這很好用,因為 toReadableNumber 只有一個引數,但在新版本中:
export function toReadableNumber(num, base = 10) {
// 默認基數為10,但可修改
}
toReadableNumber 的開發人員正在進行向后兼容的更改,添加了一個新引數,并給它一個默認值,==> 這是兼容處理,非破壞性的, 但是,他們沒想到某些代碼已經使用三個引數呼叫了該函式,
問題的根源: toReadableNumber 不是為了作為 array.map 的回呼而設計的,所以安全的做法是創建你自己的用于與 array.map 一起使用的函式:
const readableNumbers = someNumbers.map((n) => toReadableNumber(n))
就是這樣! toReadableNumber 的開發人員現在可以在不破壞我們的代碼的情況下添加引數,
同樣問題,web functions
const nextFrame = () => new Promise(requestAnimationFrame)
// 等價于
const nextFrame = () => new Promise((resolve, reject) => requestAnimationFrame(resolve, reject))
這在今天有效,因為 requestAnimationFrame 只對第一個引數執行某些操作,但這可能永遠不會正確,可能會添加一個額外的引數,并且上面的代碼可能會在任何瀏覽器提供更新的 requestAnimationFrame 時中斷,
window.requestAnimationFrame(callback)告訴瀏覽器——你希望執行一個影片,并且要求瀏覽器在下次重繪之前呼叫指定的回呼函式更新影片,該方法需要傳入一個回呼函式作為引數,該回呼函式會在瀏覽器下一次重繪之前執行
更容易發現問題的例子:
const parsedInts = ['-10', '0', '10', '20', '30'].map(parseInt)
parseInt 有兩個引數,
parseInt(string, radix)決議一個字串并回傳指定基數的十進制整數,
radix是2-36之間的整數,表示被決議字串的基數,
針對上面問題,好的寫法:
const nextFrame = () => new Promise((resolve, reject) => requestAnimationFrame(resolve))
const parsedInts = ['-10', '0', '10', '20', '30'].map((n) => parseInt(n))
同樣問題,option objects
Chrome 90 將允許您使用 AbortSignal 洗掉事件偵聽器,這意味著單個 AbortSignal 可用于洗掉事件偵聽器、取消獲取以及任何其他支持信號的內容:
const controller = new AbortController();
const { signal } = controller;
el.addEventListener('mousemove', callback, { signal });
el.addEventListener('pointermove', callback, { signal });
el.addEventListener('touchmove', callback, { signal });
// Later, remove all three listeners:
controller.abort();
壞的例子:
const controller = new AbortController()
el.addEventListener(name, callback, controller)
與回呼示例一樣,這在今天有效(因為 AbortController 和 addEventListener 選項唯一的共同點是 signal 屬性),但將來可能會中斷,
target.addEventListener(type, listener, options);options:
- capture:該型別的事件捕獲階段觸發;
- once:添加之后最多只呼叫一次(自動移除);
- passive:true 時,表示
listener永遠不會調;- signal:該
AbortSignal的abort()方法被呼叫時,監聽器會被移除,
所以,這里最好創建 addEventListener option 的物件:
const controller = new AbortController()
const options = { signal: controller.signal }
el.addEventListener(name, callback, options)
// 或者
const { signal } = controller
el.addEventListener(name, callback, { signal })
總結
在使用第三方函式時,除非是專門為當前場景所設計的,否則需要注意回呼函式和選項物件的問題!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/292197.html
標籤:其他
