初识JavaScript柯理化

原文地址:https://finget.github.io/2018/02/26/curry/

什么是柯理化

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

把含有N个参数的函数转变成,N个只有一个参数的函数。

中心思想:降低通用性,提高适用性。

通用的设计比适用的设计复杂,因此更难使用。

特点:

  • 参数复用
  • 提前返回 (return)
  • 延迟执行

参数复用

例子

瑞士军刀,上面有小剪刀,但是这个小剪刀肯定没有一个单独的剪刀好用。

function square(i) {
  return i * i;
}
function dubble(i) {
  return i * 2;
}
function dobble(i) {
  return i * 1.9;
}
function map(handeler, list) {
  return list.map(handeler);
}
// 必须要传第一个参数,才能使用map函数
console.log(map(square, [1,2,3,4,5]));
console.log(map(square, [6,7,8,9,10]));
// 容易混淆
console.log(map(dubble, [1,2,3,4,5]));
console.log(map(dobble, [1,2,3,4,5]));
console.log(map(dubble, [6,7,8,9,10]));
// 提高适用性 语义清除,方便使用
// 假设存在一个curry方法
var mapSQ = curry(map, square);
mapSQ([1,2,3,4,5]);
mapSQ([6,7,8,9,10]);

var mapDQ = curry(map, dubble);
mapDQ([1,2,3,4,5]);
mapDQ([6,7,8,9,10]);
function ajax(type, url, data) {
  var xhr = new XMLHttpRequest();
  xhr.open(type, url, true);
  xhr.send();
}
ajax('POST', 'www.baidu.com', 'name=finget');
ajax('POST', 'www.baidu.com', 'name=bios');
ajax('POST', 'www.baidu.com', 'name=mario');

// 柯理化 减少参数
var ajaxCurry = curry(ajax);

// 用POST请求
var post = ajaxCurry('POST');
post('www.baidu.com','name=finget');

var postFromBaidu = post('www.baidu.com');
postToBaidu('name=finget');

// 以上代码类似与 $.ajax => $.post / $.get
  • 参数的多少跟函数体的复杂性成正比
  • 参数的多少跟函数的维护难度成正比
  • 参数的多少跟用户的使用难度成正比

成熟的框架jquery, lodash 一个方法基本不超过4个参数。大多数就是3个或者2个参数,方法体不超过40行

一个简单的柯理化函数
function add(a,b) {
  return a + b;
}

console.log(add(5,10)); // 15

const curryAdd = function (a) {
  return function(b) {
    return a + b;
  }
}

console.log(curryAdd(5)(10)); // 15
const add5 = curryAdd(5);
// 这里就类似与var post = ajaxCurry('POST');
console.log(add5(10)); // 15

延迟执行

var fishWeight = 0;
var addWeight = function(weight){
  fishWeight += weight;
}
addWeight(2.3);
addWeight(6.5);
addWeight(1.2);
addWeight(3);

console.log(fishWeight); // 13
var curryWeight = function(fn){
  var _fishWeight = [];
  return function(){
  // apply会执行函数
  // 传入参数时,先把他们存在数组中,当没有传参就执行计算
    if (arguments.length === 0) {
      return fn.apply(null, _fishWeight);
    } else {
      // [].slice.call(arguments) 复制一下我们的arguments然后将内容加到我们的_fishWeight中
      _fishWeight = _fishWeight.concat([].slice.call(arguments));
    }
 }
}
var curryAddWeight = curryWeight(function(){
  var i = 0; len = arguments.length;
  for (i; i < len; i++) {
    fishWeight += arguments[i];
  }
})
curryAddWeight(2.3);
curryAddWeight(6.5);
curryAddWeight(1.2);
curryAddWeight(3);
// curryAddWeight(); 不加这句,console.log(fishWeight); // 0
console.log(fishWeight); // 0

柯理化后的函数是可以复用的
// 求平均值

var avgWeight = curryWeight(function(){
  var i = 0; len = arguments.length;
  for (i; i < len; i++) {
    fishWeight += arguments[i] / len;
  }
})
avgWeight(2.3);
avgWeight(6.5);
avgWeight(1.2);
avgWeight(3);
// avgWeight(); 不加这句,console.log(fishWeight); // 0
console.log(fishWeight); // 0

实现一个通用的一元curry函数

  • curry函数
function curry(fn, args) {
  var length = fn.length; // 方法参数个数 *注1
  args = args || [];
  return function(){
    var _args = args.slice(0), arg, i;
    for (i=0;i<arguments.length;i++){
      arg = arguments[i];
      _args.push(arg);
    }
    if (_args.length < length) {
      return curry.call(this, fn, _args);
    } else {
      return fn.apply(this, _args);
    }
  }
}

注1:var length = fn.length; // 方法参数个数 *注1

function add (a, b, c) {
  return a + b + c;
}
console.dir(add);

  • 使用curry函数

这个curry方法可以解决一元柯理化的场景,不是万能的

function add (a, b) {
 return a + b;
}
var curryAdd = curry(add);
var curryAdd5 = curry(add, [5]);
console.log(curryAdd(5)(10)); // 15
console.log(curryAdd5(10)); // 15
function add (a, b, c) {
  return a + b + c;
}
var curryAdd = curry(add);
var curryAdd5 = curry(add, [5]);
console.log(curryAdd(5)(10)(15)); // 30
console.log(curryAdd(5,10)(15)); // 30
console.log(curryAdd(5)(10,15)); // 30
console.log(curryAdd(5,10,15)); // 30
console.log(curryAdd5(10)(15)); // 30
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值