6 種 JavaScript 技術(shù)幫助您編寫(xiě)更簡(jiǎn)潔的代碼

英文 | https://betterprogramming.pub/6-javascript-techniques-to-help-you-write-cleaner-code-a5f867a6c750
翻譯 | 楊小愛(ài)
// simulate async operationfunction fetchMockData(name, timeToWait = 2000) {return new Promise((resolve, reject) => {setTimeout(() => {resolve({ name: name });}, timeToWait);});}const allPromises = [fetchMockData('John'), fetchMockData('Peter')];Promise.all(allPromises).then((results) => {const [first, second] = results;console.log(first, second);}).catch((err) => {console.log(err);});
順序處理多個(gè)承諾
當(dāng)您需要一個(gè)接一個(gè)地執(zhí)行多個(gè)異步請(qǐng)求時(shí),可能會(huì)有點(diǎn)棘手。您的第一直覺(jué)可能是使用 forEach 或 map,但它們沒(méi)有按預(yù)期工作。承諾不會(huì)等到它完成才開(kāi)始下一個(gè)。
不使用第 3 方庫(kù),最好的方法是使用 reduce 方法。
const allPromises = [fetchMockData('John', 4000), fetchMockData('Peter')];allPromises.reduce(async (p, curr) => {await p;return curr.then((result) => {console.log('result:', result);return curr;});}, Promise.resolve());
在上面的reduce 方法中,我們返回一個(gè)promise,它在每次迭代中解析為另一個(gè)promise。結(jié)果是一系列承諾,使異步操作一個(gè)接一個(gè)地執(zhí)行。
如輸出所示,雖然,我將第一個(gè) Promise 的超時(shí)設(shè)置為 4 秒,第二個(gè) Promise 的默認(rèn)值為 2 秒,但第一個(gè) Promise 在第二個(gè)之前先解決。

盡管reduce 本身是同步的,但它允許我們將promise 返回給累加器,這樣可以使解決方案運(yùn)行良好。
2、使用 console.time 解決性能問(wèn)題
有時(shí),我們需要調(diào)試 JavaScript 函數(shù)來(lái)分析性能。開(kāi)箱即用的 console.time 方法可以幫助我們測(cè)量執(zhí)行時(shí)間。
控制臺(tái)對(duì)象提供 time() 和 timeEnd() 方法。
首先,我們使用唯一的字符串標(biāo)簽調(diào)用 console.time() 方法,它啟動(dòng)一個(gè)計(jì)時(shí)器來(lái)跟蹤代碼執(zhí)行的持續(xù)時(shí)間。
然后,我們運(yùn)行要測(cè)量的函數(shù)。
最后,我們使用相同的標(biāo)簽調(diào)用 console.timeEnd() ,持續(xù)時(shí)間將在瀏覽器控制臺(tái)中打印出來(lái)。
如果需要調(diào)試成多步驟的代碼,可以啟動(dòng)多個(gè)定時(shí)器,用單獨(dú)的定時(shí)器測(cè)量不同的步驟,以獲得更清晰的圖景。
這是一個(gè)帶有兩個(gè)計(jì)時(shí)器的示例:
function accumlateNumbers() {let output = 0;for (var i = 1; i <= 4000000; i++) {output += i;}return output;}function callAccumlateFunction() {const timeLabel = 'Time taken accumlateNumbers';console.time(timeLabel);const output = accumlateNumbers();console.timeEnd(timeLabel);}const timeLabel2 = 'Time taken by callAccumlateFunction';console.time(timeLabel2);console.log(callAccumlateFunction());console.timeEnd(timeLabel2);
輸出是:
Time taken accumlateNumbers: 9.656005859375 msTime taken by callAccumlateFunction: 10.19677734375 ms
請(qǐng)注意,console.time 不適合需要高精度的時(shí)間測(cè)量。
3、?使用選項(xiàng)對(duì)象模式來(lái)處理傳遞給函數(shù)的多個(gè)參數(shù)
options 對(duì)象模式是為了解決向函數(shù)傳遞多個(gè)參數(shù)的問(wèn)題。
使用將參數(shù)列表傳遞給函數(shù)的正常方式,我們需要注意參數(shù)的順序。不正確的順序會(huì)造成難以檢測(cè)的缺陷。
function createUser(lastName, firstName, jobTitle, role){};// we try to create a admin usercreateUser(“John”,”Paul”,"admin", ”Manager”);
使用選項(xiàng)對(duì)象模式,我們只需要傳遞一個(gè)參數(shù),它是一個(gè)包含所有參數(shù)選項(xiàng)的命名鍵的對(duì)象。
function createUser({lastName, firstName, jobTitle, role}){};const user = {firstName: 'John',lastName: 'John',jobTitle:'Manager',role: 'Admin'};createUser(user);
如上面的代碼片段所示,不僅我們不需要擔(dān)心參數(shù)的順序,而且?guī)в羞x項(xiàng)對(duì)象模式的命名參數(shù)使代碼更易于閱讀。
選項(xiàng)對(duì)象模式通常用于四個(gè)或更多參數(shù)的情況。
4、組合多個(gè)函數(shù)
函數(shù)組合是將多個(gè)函數(shù)組合在一起,并將每個(gè)函數(shù)應(yīng)用于前一個(gè)函數(shù)的結(jié)果的方法。在正確的用例中使用時(shí),函數(shù)組合可以使您的代碼簡(jiǎn)潔優(yōu)雅。
這是一個(gè)簡(jiǎn)單的例子:
const applyFixDiscount= (x) => x?-?20const applyVipOffer = (x) => x / 2const getDiscountedPrice = (x) => applyVipOffer(applyFixDiscount(x))console.log(getDiscountedPrice(100)) // 40
上述方法有效,但當(dāng)更多功能組合在一起時(shí)將難以閱讀。更好的方法是使用下面的 compose 函數(shù)。
compose =(...fns) =>(initialVal) =>fns.reduceRight((val, fn) => fn(val), initialVal);const getDiscountedPrice = compose(applyVipOffer, applyFixDiscount);console.log('price:', getDiscountedPrice2(100)) // 40
通用的 compose 函數(shù)可以將多個(gè)函數(shù)作為輸入并一一調(diào)用。因此我們稱 compose 為高階函數(shù)。高階函數(shù)的優(yōu)勢(shì)在于它能夠以非常有表現(xiàn)力的方式組合多個(gè)操作。
請(qǐng)注意,它使用了 reduceRight,這意味著函數(shù)是從右到左執(zhí)行的。另一種方法是下面的管道方法。它使用reduce,所以順序是從左到右。
pipe = (...fns) => (initialVal) => fns.reduce((val, fn) => fn(val), initialVal);const getDiscountedPrice = pipe(applyFixDiscount, applyVipOffer);
應(yīng)用函數(shù)組合鼓勵(lì)開(kāi)發(fā)人員將程序分解為更小的部分,并將動(dòng)作或行為抽象為函數(shù)。它讓你首先考慮輸入和輸出,而不是專注于實(shí)現(xiàn)細(xì)節(jié)。
結(jié)果將是更具可讀性、可測(cè)試性和可重用性的代碼。
在實(shí)際項(xiàng)目中,函數(shù)組合的正確用例包括數(shù)據(jù)處理、復(fù)雜規(guī)則計(jì)算、工作流操作等。
5、使用解構(gòu)來(lái)提取數(shù)據(jù)
解構(gòu)是一種將值從對(duì)象屬性或數(shù)組解包到多個(gè)變量的簡(jiǎn)單而簡(jiǎn)潔的方法。
解構(gòu)的基本例子是:
const user = {name: 'John Paul',age: 23};// from object propertiesconst {name, age} = user;const count= ['one', 'two', 'three'];// array destructuringconst [first, second, third] = count;
提供了一些有用的破壞功能,包括默認(rèn)值、跳過(guò)數(shù)組元素、分配新變量名等。您可以在此處找到完整列表。
下面是一些我經(jīng)常使用的實(shí)際例子。
從函數(shù)結(jié)果中析構(gòu)
function getUser() {return {name: ‘John’, age: 24};}const {name, age} = getUser(); // name='John', age=24
拆分?jǐn)?shù)組
const [first, ...rest] = ['1', '2', '3', '4'];// output: first='1', rest=['2', '3', '4']
獲取數(shù)組的第一個(gè)元素
const fruits = [‘a(chǎn)pple’, ‘orange’, ‘pear’];[first] = fruits; // first= 'apple'
銷毀 promise.all() 的結(jié)果
Promise.all([ promise1, promise2, promise3]).then( results =>{const [first, second, third] = results;})
6、有效地使用數(shù)組
數(shù)組是我們大多數(shù)人每天處理的最常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)。以下是對(duì)數(shù)組操作的一些提示:
使用slice不變地對(duì)數(shù)組進(jìn)行排序
我們經(jīng)常想對(duì)一個(gè)數(shù)組進(jìn)行排序并得到一個(gè)不可變的副本。不幸的是, .sort 會(huì)改變?cè)紨?shù)組。使用下面的slice,我們可以在不影響原始數(shù)組的情況下獲得一個(gè)排序數(shù)組。
const newArr = arr.slice().sort()請(qǐng)注意,slice從原始數(shù)組中返回元素的淺拷貝。如果您需要進(jìn)行深度克隆,您可能喜歡使用不同的方法。
從數(shù)組中刪除重復(fù)項(xiàng)
有多種方法可以從數(shù)組中刪除重復(fù)項(xiàng)。最簡(jiǎn)單也是我最喜歡的方法是使用 Set。
Set 是在 ES6 中引入的,它表示一個(gè)唯一值列表。在下面的示例中,我們使用擴(kuò)展運(yùn)算符將 Set 操作的結(jié)果作為數(shù)組返回。
const arr = [1,2,3,2,3,4,5];console.log([...new Set(arr)]); // [1,2,3,4,5]
請(qǐng)注意 Set 方法僅適用于原始值。
從數(shù)組中過(guò)濾掉虛假值
在 JavaScript 中,假值可以是空字符串、false、0、null、NaN 或 undefined。下面是我最喜歡的從數(shù)組中過(guò)濾掉虛假值的方法。
const arrToFilter = ["user", "", 0, NaN, 9, true, undefined, "red", false];const result = mixedArr.filter(Boolean);console.log(result); // returns ["user", 9, true, "red"]
如果您之前沒(méi)有使用過(guò)它,您可能想知道 filter(Boolean) 是如何工作的?
Boolean 是一個(gè)對(duì)象包裝器。在 filter(Boolean) 方法中,數(shù)組中的每一項(xiàng)都被傳入并評(píng)估如下。結(jié)果為真或假,假值將被過(guò)濾掉。
.filter(x=> Boolean(x));使用 Array.every 和 Array.some 來(lái)簡(jiǎn)化代碼
Array.every 和 Array.some 是簡(jiǎn)化代碼的非常方便的方法。與其他方法如 forEach 或 reduce 相比,Array.every 和 Array.some 使代碼更具可讀性和簡(jiǎn)潔性。
const users = [{ name: 'john', role: 'admin' },{ name: 'peter', role: 'dev' },{ name: 'mary', role: 'dev' }];const isAllDeveloperRole = users.every(f => f.role === 'dev');const hasDeveloperRole = users.some(f => f.role === 'dev');
總結(jié)
我希望這篇文章對(duì)您有用。感謝您的閱讀。
學(xué)習(xí)更多技能
請(qǐng)點(diǎn)擊下方公眾號(hào)
![]()

