如何像高級(jí) JavaScript 開發(fā)人員一樣為一般流程編寫高階函數(shù)

一些編碼人員可能會(huì)直接更改原始功能以達(dá)到某種目的。嗯,這是初級(jí)開發(fā)人員常用的方法,也是一種直觀的方法。

但在很多情況下,它并不是最好的解決方案,并且有一些缺點(diǎn)。在今天的內(nèi)容中,我將通過示例為您介紹一些通用的解決方案。

1、once

很多時(shí)候,我們想要一個(gè)只執(zhí)行一次的函數(shù)。

比如,我們開發(fā)網(wǎng)頁的時(shí)候,總會(huì)有一些提交表單的按鈕。當(dāng)用戶點(diǎn)擊按鈕時(shí),會(huì)觸發(fā)它的 onclick 事件。

<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
</head>
<body>
 <div>
   <input type="text" username>
   <input type="password" password>
   <button id="submit">submmit</button>
 </div>
 <script> document.getElementById('submit').onclick = function(){
     console.log("sending data to the server")
   } </script>
</body>
</html></pre>

為了簡化演示問題,該示例僅記錄一條消息,而不是向服務(wù)器發(fā)送數(shù)據(jù)。

但這里有一個(gè)問題:由于網(wǎng)絡(luò)延遲,我們無法立即為用戶顯示結(jié)果。然后用戶可能繼續(xù)點(diǎn)擊該按鈕并多次向服務(wù)器提交表單。

所以,我們需要解決這個(gè)問題,你的解決方案是什么?

一個(gè)常見的解決方案是在用戶第一次單擊按鈕后禁用該按鈕。

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">document.getElementById('submit').onclick = function()
 document.getElementById('submit').disabled = true
 console.log("sending data to the server")
}</pre>

嗯,這個(gè)解決方案沒有問題。

另外,我們有一個(gè)不同的解決方案:

<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
</head>
<body>
 <div>
   <input type="text" username>
   <input type="password" password>
   <button id="submit">submmit</button>
 </div>
 <script> let hasSubmit = false
   document.getElementById('submit').onclick = function(){
     if(hasSubmit) return;
     console.log("sending data to the server")
     hasSubmit = true
   } </script>
</body>
</html></pre>

在這個(gè)解決方案中,我們使用一個(gè)標(biāo)志來記錄該函數(shù)之前是否已執(zhí)行過。

如果我們使用圖表來表示程序,它可能是這樣的:

但是,我們能否為所有此類問題找到一個(gè)通用的解決方案?

讓我們繼續(xù)一個(gè)類似的例子。很多時(shí)候,我們的程序中有一個(gè)init函數(shù)。

可以使用這個(gè)函數(shù)來設(shè)置變量、讀取配置等。這個(gè)函數(shù)應(yīng)該只執(zhí)行一次。為了確保它只執(zhí)行一次并避免意外,我們可以對(duì)函數(shù)進(jìn)行一些更改:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">let init = function(){
 console.log('init the enviorment')
}</pre>

我們可以使用這個(gè)函數(shù)來設(shè)置變量、讀取配置等。這個(gè)函數(shù)應(yīng)該只執(zhí)行一次。為了確保它只執(zhí)行一次并避免意外,我們可以對(duì)函數(shù)進(jìn)行一些更改:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">let hasInitialized = false
let init = function(){
 if(hasInitialized) return;
 console.log('init the enviorment')
 hasInitialized = true
}</pre>

好的,init函數(shù)只會(huì)初始化環(huán)境一次。

我們還可以將程序繪制成圖表。

你發(fā)現(xiàn)表單提交和初始化函數(shù)有一些共同點(diǎn)嗎?是的,他們的程序非常相似!

如果我們做高級(jí)抽象,流程應(yīng)該是這樣的:

如果該函數(shù)之前已被調(diào)用是一般程序。我們可以編寫一個(gè)高階函數(shù)來密封這個(gè)過程。

這是一次函數(shù)的實(shí)現(xiàn):

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function once(func) {
 let hasExecuted = false;
 let result;
 return function () {
   if (hasExecuted) return result;
   hasExecuted = true;
   result = func.apply(this, arguments);
   func = null;
   return result;
 };
}</pre>

現(xiàn)在,使用 once 函數(shù),我們可以輕松地歸檔執(zhí)行一次函數(shù)的目的。

提交一次:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">document.getElementById('submit').onclick = once(function()
 console.log("sending data to the server")
})</pre>

初始化一次:

好的,我們使用 once 函數(shù)來解決我們的需求。

使用 once 函數(shù)的核心思想是什么?

正如我在標(biāo)題中提到的:我們將一般過程抽象為高階函數(shù)。程序——只執(zhí)行一次函數(shù)——是一個(gè)通用過程。它會(huì)被多次使用。如果我們不做抽象,我們就必須在不同的函數(shù)中為相同的邏輯重復(fù)編寫代碼。

如果我們使用 once 函數(shù),有很多好處:

  • 我們不需要改變?cè)瓉淼墓δ堋?/li>
  • 保留業(yè)務(wù)邏輯和執(zhí)行邏輯的分隔符,這樣代碼會(huì)更易于維護(hù)。
  • 一次函數(shù)是一個(gè)可重用的函數(shù)。

2、cache

讓我們來看另一個(gè)例子。

如果有這樣的一個(gè)功能:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function compute(str) {    
   // Suppose the calculation in the funtion is very time consuming 
   console.log('2000ms have passed')
   return str.toUpperCase()
}</pre>

(其實(shí)這個(gè)案例我是從 Vue 源碼中學(xué)到的。)

我們要緩存函數(shù)操作的結(jié)果。稍后調(diào)用時(shí),如果參數(shù)相同,則不再執(zhí)行該函數(shù),而是直接返回緩存中的結(jié)果。我們能做什么?

這里有一個(gè)建議:當(dāng)你需要增強(qiáng)一個(gè)函數(shù)時(shí),不要試圖直接修改它,考慮先寫一個(gè)通用的高階函數(shù)來包裝它。

緩存函數(shù)結(jié)果的一般過程是什么?這是一個(gè)流程:

這是緩存結(jié)果的實(shí)現(xiàn):

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function cached(fn){
 // Create an object to store the results returned after each function execution.
 const cache = Object.create(null);
 // Returns the wrapped function
 return function cachedFn (str) {
   // If the cache is not hit, the function will be executed
   if ( !cache[str] ) {
       let result = fn(str);
       // Store the result of the function execution in the cache
       cache[str] = result;
   }
   return cache[str]
 }
}</pre>

現(xiàn)在我們可以使用這個(gè)緩存函數(shù)來增強(qiáng) cumpute 函數(shù):

我們做這個(gè)抽象并不是為了炫耀技巧,其實(shí)這樣的緩存功能用途廣泛。

我們知道,有一個(gè)著名的序列叫做斐波那契數(shù)列。

<pre class="prettyprint hljs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...</pre>

快速瀏覽后,您可以很容易地注意到序列的模式是每個(gè)值都是前 2 個(gè)值的總和,這意味著對(duì)于 N=5 → 2+3 或在數(shù)學(xué)中:

<pre class="prettyprint hljs lisp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">F(n) = F(n-1) + F(n-2)</pre>

現(xiàn)在我們要寫一個(gè)函數(shù):

給定一個(gè)數(shù)字N返回斐波那契數(shù)列的索引值。

怎么寫函數(shù)?

最簡單的解決方案是遞歸解決方案:

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">}function fibonacci(num) {
 if (num <= 1) return 1;
 return fibonacci(num - 1) + fibonacci(num - 2);
}</pre>

但是這個(gè)實(shí)現(xiàn)很耗時(shí),如果 num 大于 35,您將等待一段時(shí)間才能得到結(jié)果。

但是如果我們使用緩存函數(shù)來重構(gòu)實(shí)現(xiàn),我們會(huì)得到一個(gè)高性能的函數(shù)。

<pre class="prettyprint hljs lisp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">let cachedFibonacci = cached(function(num){
 if(num <= 1) return 1;
 return cachedFibonacci(num - 1) + cachedFibonacci(num - 2)
})</pre>

3、intercept

讓我們繼續(xù)。

假設(shè)您是一個(gè)庫的維護(hù)者,并且您將在未來?xiàng)売靡粋€(gè)名為 request 的舊 API。

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function request(){
 console.log('request to server')
}</pre>

在當(dāng)前版本中,您希望通過記錄消息來警告用戶 API 將被棄用。

那你會(huì)怎么做?

最糟糕的方法是在函數(shù)中添加一個(gè) console.warn 語句:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function request(){
 console.warn(`The request will be deprecated in the future`)
 console.log('request to server')
}</pre>

為什么這是最糟糕的解決方案?

您必須找到所有已棄用的 API 并對(duì)其進(jìn)行修改。這是一個(gè)非常繁瑣的過程,而且很容易導(dǎo)致錯(cuò)誤。如果沒有必要,不要更改現(xiàn)有功能。

如果我們用圖來表示程序,那就是:

正如我們?cè)谇懊鎯?nèi)容中所做的那樣,我們可以為該過程編寫一個(gè)高階函數(shù)。

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function deprecate(fn, newApi) {
 return function() {
   console.log( `The ${fn.name} will be deprecated. Please use the ${newApi} instead.`);
   return fn.apply(this, arguments);
 }
}</pre>

然后我們可以對(duì)我們的項(xiàng)目做一些改變:

,如果您的庫的用戶調(diào)用請(qǐng)求函數(shù),他們將收到一條消息。

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">// index.js
importre request from './request';
const _request = deprecate(request, 'fetch');
export {
 request: _request
}
現(xiàn)在</pre>

好的,讓我們繼續(xù)一個(gè)類似的例子。

我們有一個(gè) fetch 函數(shù)來向服務(wù)器發(fā)送請(qǐng)求。它將返回 HTML 文本或 JSON 格式的文本。

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">var fetch = function(url){
 let responseContent = null
 console.log(`fetching ${url}`)
 if(Math.random() < 0.5){
   return '<html><body>hello world</body></html>'
 } else {
   return '{"name": "bytefish"}'
 }
}</pre>

我們現(xiàn)在要做的是,如果我們發(fā)現(xiàn)響應(yīng)結(jié)果是 JSON 格式的字符串,我們將其轉(zhuǎn)換為 JSON 對(duì)象。如果是其他格式的字符串,則不進(jìn)行處理。我應(yīng)該怎么辦?

老規(guī)矩,先畫個(gè)圖:

具體原理已經(jīng)解釋過很多次了,這里我直接給出一個(gè)高階函數(shù):

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function toJSON(fn) {
 return function() {
   let res = fn.apply(this, arguments)
   try{
     let json = JSON.parse(res)
     return json
   } catch(e){
     return res
   }
 }
}</pre>

用法:

這兩個(gè)例子有點(diǎn)簡單。但附近還有一個(gè)更重要的想法。

  • derecate功能旨在在執(zhí)行原始功能之前執(zhí)行某些操作。
  • toJSON函數(shù)旨在執(zhí)行原始函數(shù)后執(zhí)行某些操作。

我們能把這個(gè)過程抽象成一個(gè)新的高階函數(shù)嗎?

我們當(dāng)然可以。

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function intercept(fn, {before = null, after = null}) {
 return function () {
   if(before != null) {
     before.apply(this, arguments)
   }
   const result = fn.apply(this, arguments)
   if(after != null){
     after.call(this, result)
   }
   return result
 };
}</pre>

如果你之前用過 Axios 這個(gè)著名的 HTTP 請(qǐng)求庫,你就會(huì)知道 Axios 有一個(gè)攔截器 API 供用戶攔截請(qǐng)求和響應(yīng)。

4、Batch

好的,這是我們的最后一個(gè)例子。

這是一個(gè)將輸入加倍的函數(shù)。

<pre class="prettyprint hljs lua" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function double(num){
 return num * 2
}</pre>

嗯,很簡單的功能,只是為了演示。

如果我們想讓這個(gè)函數(shù)接受一個(gè)數(shù)組作為參數(shù),那么將數(shù)組中所有元素的值加倍,然后返回一個(gè)新數(shù)組。你怎么寫代碼?

我們可以這樣寫:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function double(nums){
 return nums.map(num =>  num * 2)
}</pre>

確實(shí)可以這樣寫。

但遺憾的是,JavaScript 沒有函數(shù)重載,后者的函數(shù)會(huì)覆蓋前者。為了讓我們的double函數(shù)同時(shí)處理兩種參數(shù)類型,我們必須在函數(shù)體中做出判斷:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function double(arg){
 if(Array.isArray(arg)){
   return nums.map(num =>  num * 2)
 }
 return num * 2
}</pre>

我們想要的是為所有這些問題創(chuàng)建一個(gè)通用的解決方案:一個(gè)高階函數(shù),可以標(biāo)記一個(gè)函數(shù)來處理單個(gè)參數(shù)或類似數(shù)組的參數(shù)。

這是一個(gè)實(shí)現(xiàn):

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function batch(fn) {
 return function(subject, ...args) {
   if(Array.isArray(subject)) {
     return subject.map((s) => {
       return fn.call(this, s, ...args);
     });
   }
   return fn.call(this, subject, ...args);
 }
}</pre>

總結(jié)

我想,我舉的例子已經(jīng)夠多了。無論是once,cache,intercept還是batch,它們都對(duì)某個(gè)進(jìn)程進(jìn)行了一些抽象。

  • 我們想要一個(gè)只執(zhí)行一次的函數(shù),我們可以用 abstract once。
  • 我們想要一個(gè)函數(shù)來緩存相應(yīng)參數(shù)的結(jié)果,我們可以 abstract cache 。
  • 我們想要一個(gè)在執(zhí)行前后做某事的函數(shù),我們 可以 abstract intercept。
  • 我們想要一個(gè)通過參數(shù)類型改變其執(zhí)行流程的函數(shù),我們可以 abstract batch。
  • 它們都遵循一個(gè)共同的范式:即使用高階函數(shù)來abstract 任何一般過程。

Nested

恩,我想提的最后一件事:如果有必要,我們可以嵌套這些高階函數(shù)。

假設(shè)我們不僅要緩存計(jì)算函數(shù)的結(jié)果,還要在執(zhí)行它之前記錄它的參數(shù),并在執(zhí)行它之后記錄它的結(jié)果。然后,我們還想讓它能夠處理多重參數(shù)。我們可以這樣寫:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">let computedEnhance = batch(intercept(cached(computed), {
 before: arg => {
   console.log(`processing ${arg}`)
 },
 after: res => {
   console.log(`returned ${res}`)
 }
}))</pre>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容