理解Javascript的柯里化

前言

本文1454字,閱讀大約須要4分鐘。前端

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

事親以敬,美過三牲。函數式編程

正文

函數式編程是一種現在比較流行的編程範式,它主張將函數做爲參數進行傳遞,而後返回一個沒有反作用的函數,說白了,就是但願一個函數只作一件事情。函數

像Javascript,Haskell,Clojure等編程語言都支持函數式編程。工具

這種編程思想涵蓋了三個重要的概念:this

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

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

什麼是柯里化

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

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」,可領取一攬子前端技術書籍;

https://image.damonare.cn/cainiaoxueqianduan.png

相關文章
相關標籤/搜索