一文快速瞭解JS中的柯里化(Currying)

2022-02-25 13:00:31
本篇文章帶大家快速瞭解一下Javascript中的柯里化(Currying),有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

柯里化將多引數函數轉換為一元(單引數)函數。【相關推薦:

柯里化函數一次接受多個引數。所以如果你有

greet = (greeting, first, last) => `${greeting}, ${first} ${last}`;

greet('Hello', 'Bruce', 'Wayne'); // Hello, Bruce Wayne

可以寫成這種形式

curriedGreet = curry(greet);

curriedGreet('Hello')('Bruce')('Wayne'); // Hello, Bruce Wayne

如何正確的使用?

正確的使用「柯里化」是因為某些curry函數在使用上更加靈活。Currying 在理論上很棒,但是在 JavaScript 中為每個引數呼叫一個函數會很累。

Ramda 的 curry函數可以讓你curriedGreet像這樣呼叫:

// greet requires 3 params: (greeting, first, last)

// these all return a function looking for (first, last)
curriedGreet('Hello');
curriedGreet('Hello')();
curriedGreet()('Hello')()();

// these all return a function looking for (last)
curriedGreet('Hello')('Bruce');
curriedGreet('Hello', 'Bruce');
curriedGreet('Hello')()('Bruce')();

// these return a greeting, since all 3 params were honored
curriedGreet('Hello')('Bruce')('Wayne');
curriedGreet('Hello', 'Bruce', 'Wayne');
curriedGreet('Hello', 'Bruce')()()('Wayne');

請注意,你可以選擇一次性給出多個引數。此實現在編寫程式碼時更有用。

如上所示,你可以在沒有引數的情況下永遠呼叫此函數,並且它將始終返回一個需要剩餘引數的函數。

工作原理相同

const curry = (f, arr = []) => (...args) =>
  ((a) => (a.length === f.length ? f(...a) : curry(f, a)))([...arr, ...args]);

讓我們一起重構和欣賞它。

我還在debuggerChrome 開發人員工具中新增了一些語句來檢查它。

curry = (originalFunction, initialParams = []) => {
  debugger;

  return (...nextParams) => {
    debugger;

    const curriedFunction = (params) => {
      debugger;

      if (params.length === originalFunction.length) {
        return originalFunction(...params);
      }

      return curry(originalFunction, params);
    };

    return curriedFunction([...initialParams, ...nextParams]);
  };
};

開始

貼上greetcurry進入您的控制檯。然後進入curriedGreet = curry(greet)並開始瘋狂。

在第 2 行暫停

1.png

檢查我們看到的兩個引數,originalFunction並且greet預設initialParams為空陣列,因為我們沒有提供它。移動到下一個斷點,哦等等……就是這樣。 是的!curry(greet)只返回一個需要 3 個以上引數的新函數。在控制檯中輸入curriedGreet以檢視我在說什麼。

當你玩完這個之後,讓我們變得更瘋狂一點,然後做
sayHello = curriedGreet('Hello').

在第 4 行暫停

2.png

在繼續之前,在控制檯中輸入originalFunction和。initialParams請注意,即使我們在一個全新的函數中,我們仍然可以存取這兩個引數?這是因為從父函數返回的函數享有其父函數的作用域。

繼承

在父函數傳遞之後,他們將引數留給孩子使用。有點像現實生活中的繼承。

curry最初給出originalFunctioninitialParams然後返回一個「子」函數。這兩個變數還沒有被處理掉,因為也許那個孩子需要它們。如果他不這樣做,那麼這個範圍就會被清理乾淨,因為當沒有人提到你時,那就是你真正死去的時候。

好的,回到第 4 行……

3.png

檢查nextParams並看到它是['Hello']……一個陣列?但我以為我們說curriedGreet(‘Hello’) ,不是curriedGreet(['Hello'])

正確:我們呼叫curriedGreet了 with 'Hello',但是多虧了rest 語法,我們變成 'Hello'['Hello'].

是嗎?!

curry是一個通用函數,可以提供 1、10 或 10,000,000 個引數,因此它需要一種方法來參照所有引數。使用類似的 rest 語法捕獲一個陣列中的每個引數,使curry' 的工作更容易。

讓我們跳到下debugger一條語句。

現在是第 6 行,但請稍等。

您可能已經注意到第 12 行實際上在debugger第 6 行的語句之前執行。如果不是,請仔細檢視。我們的程式在第 5 行定義了一個呼叫函數curriedFunction,在第 12 行使用它,然後我們debugger在第 6 行點選了該語句。curriedFunction呼叫的是什麼?

[...initialParams, ...nextParams];

呸呸呸。檢視params第 5 行,您會看到['Hello']. 兩者initialParams和都是陣列,所以我們使用方便的擴充套件運運算元nextParams將它們展平並組合成一個陣列。

這就是好事發生的地方。

5.png

第 7 行說「如果paramsoriginalFunction長度相同,請greet使用我們的引數呼叫,我們就完成了。」 這使我想起…

JavaScript 函數也有長度

這就是curry它的魔力!這就是它決定是否要求更多引數的方式。

在 JavaScript 中,函數的 .length屬性告訴你它需要多少個引數

greet.length; // 3

iTakeOneParam = (a) => {};
iTakeTwoParams = (a, b) => {};

iTakeOneParam.length; // 1
iTakeTwoParams.length; // 2複製程式碼

如果我們提供的和預期的引數匹配,我們很好,只需將它們交給原始函數並完成工作!

但是在我們的例子中,引數和函數長度是一樣的。我們只提供了‘Hello’,所以params.length是 1,並且originalFunction.length是 3 因為greet需要 3 個引數:greeting, first, last

那麼接下來會發生什麼?

好吧,由於該if語句的計算結果為false,程式碼將跳到第 10 行並重新呼叫我們的主curry函數。它重新接收greet,這一次,'Hello'並重新開始瘋狂。

這就是遞迴,我的朋友們。

curry本質上是一個無限迴圈的自呼叫,引數飢渴的函數,直到他們的客人滿了才會休息。熱情好客。

6.png

回到第 2 行

與以前相同initialParams的引數,除了['Hello']這次。再次跳過以退出迴圈。在控制檯中輸入我們的新變數,sayHello. 這是另一個函數,仍然期待更多引數,但我們正在變得更加溫暖......

讓我們把火調大sayHelloToJohn = sayHello('John')

我們又在第 4 行了,而且nextParams['John']。跳到第 6 行的下一個偵錯程式並檢查params:它是['Hello', 'John']!?

7.png

為什麼?

因為請記住,第 12 行說「嘿curriedFunction,他'Hello'上次和‘John’這次都給了我。把他們兩個都帶進這個陣法[...initialParams, ...nextParams]。」

8.png

現在curriedFunction再次將length這些params與進行比較originalFunction,因為2 < 3我們移動到第 10 行並curry再次呼叫!當然,我們傳遞greet了我們的 2 個引數,['Hello', 'John']

9.png

我們已經很接近了,讓我們完成這一切,並得到完整的問候!

sayHelloToJohnDoe = sayHelloToJohn('Doe')

我想我們知道接下來會發生什麼。

10.png

11.png

12.png

結論

greet得到他的引數,curry停止迴圈,我們收到了我們的問候:Hello, John Doe.

多玩一些這個功能。嘗試一次提供多個引數或不提供引數,隨心所欲地瘋狂。curry檢視在返回預期輸出之前必須遞迴多少次。

curriedGreet('Hello', 'John', 'Doe');
curriedGreet('Hello', 'John')('Doe');
curriedGreet()()('Hello')()('John')()()()()('Doe');

【相關視訊教學推薦: 】

以上就是一文快速瞭解JS中的柯里化(Currying)的詳細內容,更多請關注TW511.COM其它相關文章!