前端基础进阶(八):深入详解函数的柯里化

https://segmentfault.com/a/1190000012646488  https://yangbo5207.github.io/wutongluo/

说明:此处只是记录阅读前端基础进阶的理解和总结,如有需要请阅读上面的链接

一、柯理化的概念

柯理化是指这样一种现象:函数createCurry接受另一个函数A作为参数,返回另一个函数_A,并且_A能够处理A的剩余参数。

函数_A叫做函数A的柯理化函数,createCurry叫做柯理化函数通用式,用于产生柯理化函数

概念解释:

假设有一个接收三个参数的函数A。

function A(a, b, c) {
    // do something
}

还有一个封装好的柯理化函数通用式createCurry,它接受函数A作为参数,返回一个新的函数_A

var _A = createCurry(A);

_A作为函数A的柯理化函数,他能够处理A的剩余参数。因此下面的运行结果都是等价的。

_A(1, 2, 3);
_A(1, 2)(3);
_A(1)(2, 3);
_A(1)(2)(3);
A(1, 2, 3);

_A能够处理A的所有剩余参数,因此柯里化也被称为部分求值。

二、柯理化通用式

柯里化函数的运行过程其实是一个参数的收集过程,我们将每一次传入的参数收集起来,并在最里层里面处理。因此我们在实现createCurry时,可以借助这个思路来进行封装。

// 简单实现,参数只能从右到左传递
function createCurry(func, args) {

    var arity = func.length;
    var args = args || [];

    return function() {
        var _args = [].slice.call(arguments);
        [].push.apply(_args, args);

        // 如果参数个数小于最初的func.length,则递归调用,继续收集参数
        if (_args.length < arity) {
            return createCurry.call(this, func, _args);
        }

        // 参数收集完毕,则执行func
        return func.apply(this, _args);
    }
}

使用上面的柯理化通用式就能生成大部分符合要求的柯理化函数。例如想要检查输入的电话号码和email是否符合规则,可以借助柯理化函数这样做

function check(targetString, reg) {
    return reg.test(targetString);
}
var _check = createCurry(check);

var checkPhone = _check(/^1[34578]\d{9}$/);//检查电话号码
var checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);//检查email

console.log(checkPhone('183888888'));
console.log(checkEmail('xxxxx@test.com'));

 

 

三、无限参数的柯里化

在前端面试中,你可能会遇到这样一个涉及到柯里化的题目。

// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

这个题目的目的是想让add执行之后返回一个函数能够继续执行,最终运算的结果是所有出现过的参数之和。而这个题目的难点则在于参数的不固定。我们不知道函数会执行几次。因此我们不能使用上面我们封装的createCurry的通用公式来转换一个柯里化函数。在此之前,需要了解两个重要的知识点。

1)不定参数

如果想把一个数组中的元素展开传递给一个函数需要怎么做

// 大家可以思考一下,如果将args数组的子项展开作为add的参数传入
function add(a, b, c, d) {
    return a + b + c + d;
}
var args = [1, 3, 100, 1];

一种方法是使用apply 

add.apply(null, args);  // 105

另一种方法就是使用ES6中的不定参数

add(...args)

两种写法是等效的。

2)函数的隐式转换。当我们直接将函数参与其他的计算时,函数会默认调用toString方法,直接将函数体转换为字符串参与计算。

function fn() { return 20 }
console.log(fn + 10);     // 输出结果 function fn() { return 20 }10

我们可以重写函数的toString方法,让函数参与计算时,输出我们想要的结果。

function fn() { return 20; }
fn.toString = function() { return 30 }

console.log(fn + 10); // 40

 

 除此之外,当我们重写函数的valueOf方法也能够改变函数的隐式转换结果。

function fn() { return 20; }
fn.valueOf = function() { return 60 }

console.log(fn + 10); // 70

当我们同时重写函数的toString方法与valueOf方法时,最终的结果会取valueOf方法的返回结果。

function fn() { return 20; }
fn.valueOf = function() { return 50 }
fn.toString = function() { return 30 }

console.log(fn + 10); // 60

补充了这两个知识点之后,我们可以来尝试完成之前的题目了

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = [].slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var adder = function () {
        var _adder = function() {
            // [].push.apply(_args, [].slice.call(arguments));
            _args.push(...arguments);
            return _adder;
        };

        // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
        _adder.toString = function () {
            return _args.reduce(function (a, b) {
                return a + b;
            });
        }

        return _adder;
    }
    // return adder.apply(null, _args);
    return adder();
}

var a = add(1)(2)(3)(4);   // f 10
var b = add(1, 2, 3, 4);   // f 10
var c = add(1, 2)(3, 4);   // f 10
var d = add(1, 2, 3)(4);   // f 10

// 可以利用隐式转换的特性参与计算
console.log(a + 10); // 20
console.log(b + 20); // 30
console.log(c + 30); // 40
console.log(d + 40); // 50

// 也可以继续传入参数,得到的结果再次利用隐式转换参与计算
console.log(a(10) + 100);  // 120
console.log(b(10) + 100);  // 120
console.log(c(10) + 100);  // 120
console.log(d(10) + 100);  // 120
// 其实上栗中的add方法,就是下面这个函数的柯里化函数,只不过我们并没有使用通用式来转化,而是自己封装
function add(...args) {
    return args.reduce((a, b) => a + b);
}

 

转载于:https://www.cnblogs.com/lidaying5/p/8658714.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值