1. 程式人生 > >理解Javascript的柯里化

理解Javascript的柯里化

前言

本文1454字,閱讀大約需要4分鐘。

總括: 本文以初學者的角度來闡述Javascript中柯里化的概念以及如何在工作中進行使用。

  • 原文地址:理解Javascript的柯里化

  • 知乎專欄: 前端進擊者

  • 博主部落格地址:Damonare的個人部落格

事親以敬,美過三牲。

正文

函數語言程式設計是一種如今比較流行的程式設計正規化,它主張將函式作為引數進行傳遞,然後返回一個沒有副作用的函式,說白了,就是希望一個函式只做一件事情。

像Javascript,Haskell,Clojure等程式語言都支援函數語言程式設計。

這種程式設計思想涵蓋了三個重要的概念:

  • 純函式
  • 柯里化
  • 高階函式

而這篇文章主要是想向大家講清楚柯里化這個概念。

什麼是柯里化

首先我們先來看一個例子:

function sum(a, b, c) {
  return a + b + c;
}
// 呼叫
sum(1, 2, 3); // 6

上述函式實現的是將a,b,c三個引數相加,改寫為柯里化函式如下:

function sum(a) {
  return function (b) {
    return function(c) {
        return a + b + c;
        } 
    }
}
// 呼叫
let sum1 = sum(1);
let sum2 = sum1(2);
sum2(3); // 6

所謂柯里化就是把具有較多引數的函式轉換成具有較少引數的函式的過程。

我們來一步步看上面那個柯里化函式做了什麼,首先第一步呼叫了sum(1),此時變數sum1相當於:

sum1 = function(b) {
    return function(c) {
    // 注意此時變數a存在於閉包中,可以呼叫,a = 1
    return a + b + c;
  }
}

然後呼叫sum1(2),此時賦值給變數sum2相當於:

sum2 = function(c) {
  // 變數a,b皆在閉包中, a = 1, b = 2
  return a + b + c;
}

最後呼叫sum2(3),返回1 + 2 + 3的結果6;

這就是一個最簡單的柯里化函式,是不是很簡單呢?

柯里化函式的作用

那麼問題來了,上面改寫後的柯里化函式和原函式比起來程式碼多了不少,而且也不如原函式好理解,柯里化函式到底有什麼用呢?

確實,柯里化函式在這裡看起來的確是很臃腫,不實用,但在很多場景下他的作用是很大的,甚至很多人在不經意間已經在使用柯里化函數了。舉一個簡單的例子:

假設我們有一批的長方體,我們需要計算這些長方體的體積,實現一個如下函式:

function volume(length, width, height) {
    return length * width * height;
}
volume(200, 100, 200);
volume(200, 150, 100);
volume(200, 50, 80);
volume(100, 50, 60);

如上計算長方體的體積函式會發現存在很多相同長度的長方體,我們再用柯里化函式實現一下:

function volume(length, width, height) {
    return function(width) {
        return function(height) {
         return length * width * height;
            }
    }
}
let len200 = volume(200);
len200(100)(200);
len200(150)(100);
len200(50)(80);
volume(100)(50)(60);

如上,通過實現一個len200函式我們統一處理長度為200的長方體的體積,這就實現了引數複用。

我們再舉一個只執行一次函式的例子:

function execOnce(fun) {
    let flag = true;
  return function() {
    if (flag) {
      fun && fun();
      flag = false;
    }
  }
}
let onceConsole = execOnce(function() {
    console.log('只打印一次');
});
onceConsole();
onceConsole();

如上,我們實現了一個execOnce函式,該函式接受一個函式引數,然後返回一個函式,變數flag存在閉包中,用來判斷返回的函式是否執行過,onceConsole相當於:

let onceConsole = function() {
  if (flag) {
        (function() {
        console.log('只打印一次');
      })()
      flag = false;
    }
}

這也是柯里化函式的一個簡單應用。

通用柯里化函式的實現

既然柯里化函式這麼實用,那麼我們能不能實現一個通用的柯里化函式呢?所謂通用,就是說該函式可以把函式引數轉換為柯里化函式,看下第一版實現的程式碼:

 // 第一版
var curry = function (fn) {
  var args = [].slice.call(arguments, 1);
  return function() {
    var newArgs = args.concat([].slice.call(arguments));
    return fn.apply(null, newArgs);
  };
};
 function add(a, b) {
   return a + b;
 }

var addFun = curry(add, 1, 2);
addFun() // 3
//或者
var addOne = curry(add, 1);

如上程式碼,我們接受一個函式作為引數,然後收集其它的引數,將這些引數傳給這個函式引數去執行。但上面的程式碼有個問題,引數不夠自由,比如我們想這麼呼叫就會報錯:

var addFun = curry(function(a, b,c) {
  return a + b + c;
}, 1);
addFun(2)(3); // 報錯 addFun(...) is not a function

這好像違背了我們引數複用的原則,改進如下:

function curry(fn, args) {
  var length = fn.length;
  args = args || [];
  return function(...rest) {
    var _args = [...args, ...rest];
    return _args.length < length
      ? curry.call(this, fn, _args)
    : fn.apply(this, _args);
  }
}
var fn = curry(function(a, b, c) {
  console.log(a + b + c);
});
fn('a', 'b', 'c'); // abc
fn('a', 'b')('c'); // abc
fn('a')('b')('c'); // abc

如上實現就很完善,該工具函式的實現總結起來就一句話:

利用閉包將函式的引數儲存起來,等引數達到一定數量時執行函式。

後記

柯里化是以閉包為基礎的,不理解閉包可能對柯里化的理解有所阻礙,希望通過這篇文章能讓各位瞭解和理解Javascript的柯里化。

能力有限,水平一般,歡迎勘誤,不勝感激。


訂閱更多文章可關注「菜鳥學前端」,回覆「666」,獲取一攬子前端技術書籍

  • 回覆「666」,可領取一攬子前端技術書籍;

相關推薦

從 ES6 高階箭頭函式理解函式

前言:第一次看到多個連續箭頭函式是在一個 react 專案中,然鵝確認了下眼神,並不是對的人,因為看得一臉懵逼。em......於是開始各種搜尋,先是知道了多個連續箭頭函式就是 es6 的多次柯里化的寫法,對於函式柯里化,很久以前就知道這個名次,但是並不理解,也沒有去了解。為了弄明白多個連續箭頭函式,開始

【譯】理解JavaScript中的

譯文開始 函數語言程式設計是一種程式設計風格,這種程式設計風格就是試圖將傳遞函式作為引數(即將作為回撥函式)和返回一個函式,但沒有函式副作用(函式副作用即會改變程式的狀態)。 有很多語言採用這種程式設計風格,其中包括JavaScript、Haskell、Clojure、Erlang和Scala等一些很流行的程

理解Javascript

前言 本文1454字,閱讀大約需要4分鐘。 總括: 本文以初學者的角度來闡述Javascript中柯里化的概念以及如何在工作中進行使用。 原文地址:理解Javascript的柯里化 知乎專欄: 前端進擊者 博主部落格地址:Damonare的個人部落格 事親以敬,美過三牲。 正文 函數語言程式設計是一

深入理解scala的( currying or curry )以及其用處

百度百科定義: 柯里化(Currying)是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數且返回結果的新函式的技術。 從數學的角度講,這是一個對函式消元求解的過程: def f(x:Int,y:Int)=x+y def g(x:Int

JavaScript中的

今天在部落格園首頁看到一篇好文章 【譯】理解JavaScript中的柯里化 加上最近工作中的一些感悟,算是對函數語言程式設計語言(scala, python, javascrtpt)中的閉包,偏函式、柯里化有了更進一步的認識。 之前學Scala被繞的雲裡霧裡的各種名詞,現在也開始慢慢理解了。

JavaScript筆記:,資料扁平

柯里化,扁平化 柯里化 扁平化 柯里化 在數學和電腦科學中,柯里化是一種將使用多個引數的一個函式轉換成一系列使用一個引數的函式的技術。 前端使用柯里化主要是為了簡化程式碼結構。提高系統的可維護性,一個方法,只有一個引數,強制了功能的單一性,使

[譯] JavaScript中的函式

原文 Currying in JS 函式柯里化 函式柯里化以Haskell Brooks Curry命名,柯里化是指將一個函式分解為一系列函式的過程,每個函式都只接收一個引數。(譯註:這些函式不會立即求值,而是通過閉包的方式把傳入的引數儲存起來,直到真正需要的時候才會求值) 柯里化例子

JavaScript中的(curry)

一、什麼是柯里化? 柯里化是一個轉換過程,把一個接受多個引數的函式,轉換成一個接受一個單一引數的函式,並且返回一個函式,返回的函式接受其餘的引數,並返回結果。 舉個例子: // 無柯里化 var example1 = function (a, b, c

掌握JavaScript函式的(轉載)

看了之後,突然之間對別的語言有了好奇想去看看了。 – ITDogFire duckywang Haskell和scala都支援函式的柯里化,JavaScript函式的柯里化還與JavaScript的函式程式設計有很大的聯絡,如果你感興趣的話,可以在

函式之Javascript

定義 根據定義來說,柯里化就是將一個接收“多個”引數的函式拆分成一個或者許多個接收“單一”引數的函式。定義看起來是比較抽象的,下面來舉個例子: 程式碼 12345 function concat(str1,str2){ return str1 + str2;

javascript中bind繫結接收者與函式

如果我要遍歷一個數組, 我只要給forEach傳一個匿名函式即可,很簡單; let arr = ['a', 'b', 'c']; arr.forEach((item, index) => { console.log(item); console.log(index); })

簡單粗暴詳細講解javascript實現函式與反

    函式柯里化(黑人問號臉)???Currying(黑人問號臉)???妥妥的中式翻譯既視感;下面來一起看看究竟什麼是函式柯里化:     維基百科的解釋是:把接收多個引數的函式變換成接收一個單一引數(最初函式的第一個引數)的函式,並返回接受剩餘的引數而且返回結果的新函式的技術。其由數學家Haskell B

JavaScript ES6函數語言程式設計(二):、偏應用、組合、管道

上一篇介紹了閉包和高階函式,這是函數語言程式設計的基礎核心。這一篇來看看高階函式的實戰場景。 首先強調兩點: 注意閉包的生成位置,清楚作用域鏈,知道閉包生成後快取了哪些變數 高階函式思想:以變數作用域作為根基,以閉包為工具來實現各種功能 柯里化(curry) 定義:柯里化是把一個多引數函式轉換為一個巢狀的

函式 - 函式鏈式呼叫 - lazyman

實現鏈式呼叫實現 add函式,add(1)(2)(3)(4)輸出10,然後考慮拓展性 // 解析為add(1)返回函式A,A(2)返回函式B, B(3)返回函式C function add(num) { var sum = 0; sum += num; ret

大資料之scala(三) --- 類的檢查、轉換、繼承,檔案,特質trait,操作符,apply,update,unapply,高階函式,,控制抽象,集合

一、類的檢查和轉換 -------------------------------------------------------- 1.類的檢查 isInstanceOf -- 包括子類 if( p.isInstanceOf[Employee]) {

scala高階語法之(curring)和 隱式轉換(implicit)

柯里化(curring)和 隱式轉換(implicit) 柯里化(curring) scala 中 curring 是將一個正常的方法轉換為科裡化的一個過程 把一個引數列表中的多個引數轉換為多個列表 如:①→② ① def m1(a:Int,b:Int)=a+b

函式(currying)

currying的好處 Little pieces can be configured and reused with ease, without clutter. Functions are used throughout. 例子1 let

Scala基礎:閉包、、隱式轉換和隱式引數

閉包,和js中的閉包一樣,返回值依賴於宣告在函式外部的一個或多個變數,那麼這個函式就是閉包函式。 val i: Int = 20 //函式func的方法體中使用了在func外部定義的變數 那func就是個閉包函式 val func = (x: Int) => x +

溫故而知新: 與 bind()的認知

  什麼是柯里化?科裡化是把一個多引數函式轉化為一個巢狀的一元函式的過程。(簡單的說就是將函式的引數,變為多次入參) const curry = (fn, ...args) => fn.length <=&n

JS函數語言程式設計 - 函式組合與

我們都知道單一職責原則,其實面向物件的SOLID中的S(SRP, Single responsibility principle)。在函式式當中每一個函式就是一個單元,同樣應該只做一件事。但是現實世界總是複雜的,當把現實世界對映到程式設計時,單一的函式就沒有太大的意義。這個時候就需要函式組合和柯里化了。 鏈