JS 函数柯里化

在 JavaScript 开发中,函数柯里化是一个非常重要的概念,它不仅可以提升代码的复用性和可读性,还能在某些场景下优化性能。本文将深入探讨函数柯里化的原理、实现方法以及实际应用场景。

一、什么是函数柯里化

函数柯里化(Currying)是一种将接受多个参数的函数变换成接受一个单一参数的函数的技术。这个技术的核心思想是:当你固定某些参数后,返回一个新的函数,这个新函数接受余下的参数,并最终返回结果。

二、柯里化的直观理解

假设我们有一个求取两个数之和的函数:

javascript 复制代码
function add(x, y) {
    return x + y;
}
console.log(add(1, 2)); // 3
console.log(add(5, 7)); // 12

在上面的示例中,add 函数接收两个参数,返回它们的和。现在,我们对 add 函数进行柯里化改造:

javascript 复制代码
function add(x) {
    return function (y) {
        return x + y;
    };
}
console.log(add(1)(2)); // 3
console.log(add(5)(7)); // 12

通过柯里化,add 函数现在只接受一个参数,但返回的是一个新函数,这个新函数接收第二个参数,并最终返回结果。这种改造利用了闭包的特性,将第一个参数 x 保存在闭包中,直到第二个参数 y 被传入时,才执行最终的计算。

三、函数柯里化的实际应用

1. 参数复用

柯里化的一个重要应用是参数复用,即将相同的参数固定下来。例如,我们有一个正则校验函数:

javascript 复制代码
function check(reg, txt) {
    return reg.test(txt);
}
console.log(check(/\d+/g, 'test1')); // true
console.log(check(/\d+/g, 'testtest')); // false
console.log(check(/[a-z]+/g, 'test')); // true

如果我们在多个地方都需要校验是否有数字,我们可以将正则表达式通过柯里化固定下来:

javascript 复制代码
function curryingCheck(reg) {
    return function (txt) {
        return reg.test(txt);
    };
}

var hasNumber = curryingCheck(/\d+/g);
var hasLetter = curryingCheck(/[a-z]+/g);

console.log(hasNumber('test1')); // true
console.log(hasNumber('testtest')); // false
console.log(hasLetter('21212')); // false

通过柯里化,我们避免了重复传递相同的正则表达式,使代码更加简洁和易于维护。

2. 提前确认

柯里化还可以用于提前确认某些参数。例如,在绑定事件时,我们通常需要判断浏览器是否支持 addEventListener。通过柯里化,我们可以提前确定这一点:

javascript 复制代码
var on = (function () {
    if (document.addEventListener) {
        return function (element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    } else {
        return function (element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler);
            }
        };
    }
})();

on(div, 'click', function () {});

通过柯里化,我们避免了每次调用 on 函数时都进行浏览器兼容性检查,提高了代码的效率。

四、封装通用柯里化函数

接下来,我们来封装一个通用的柯里化函数。这个函数可以将任意多参数函数转换为柯里化函数:

javascript 复制代码
function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);
    if (args.length >= fn.length) {
        return fn.apply(this, args);
    }
    function _curry() {
        args.push(...arguments);
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        }
        return _curry;
    }
    return _curry;
}

测试一下这个通用柯里化函数:

javascript 复制代码
function add(a, b, c) {
    return a + b + c;
}

console.log(curry(add)(1)(2)(3)); // 6
console.log(curry(add, 1)(2)(3)); // 6
console.log(curry(add, 1, 2, 3)); // 6
console.log(curry(add, 1)(3, 4)); // 8

var addCurrying = curry(add)(2);
console.log(addCurrying(7)(8)); // 17

五、经典的柯里化面试题

实现一个 add 方法,使计算结果能够满足如下预期:

javascript 复制代码
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

要完成这个需求,我们可以使用柯里化函数:

javascript 复制代码
function add() {
    var _args = Array.prototype.slice.call(arguments);

    var _adder = function () {
        _args.push(...arguments);
        return _adder;
    };

    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    };

    return _adder;
}

console.log(add(1)(2)(3).toString()); // 6
console.log(add(1, 2, 3)(4).toString()); // 10
console.log(add(1)(2)(3)(4)(5).toString()); // 15
console.log(add(2, 6)(1).toString()); // 9
相关推荐
心在飞扬4 分钟前
ReRank重排序提升RAG系统效果
前端·后端
心在飞扬9 分钟前
RAPTOR 递归文档树优化策略
前端·后端
zone773918 分钟前
003:RAG 入门-LangChain 读取图片数据
后端·python·面试
前端Hardy26 分钟前
别再无脑用 `JSON.parse()` 了!这个安全漏洞你可能每天都在触发
前端·javascript·vue.js
前端Hardy28 分钟前
别再让 `console.log` 上线了!它正在悄悄拖垮你的生产系统
前端·javascript·vue.js
zone773931 分钟前
002:RAG 入门-LangChain 读取文本
后端·算法·面试
青青家的小灰灰32 分钟前
从入门到精通:Vue3 ref vs reactive 最佳实践与底层原理
前端·vue.js·面试
OpenTiny社区44 分钟前
我的新同事是个AI:支持skill后,它用TinyVue搭项目还挺溜!
前端·vue.js·ai编程
心在飞扬1 小时前
MultiVector 多向量检索
前端·后端
用户39051332192881 小时前
async 函数返回的 Promise 状态何时变为 resolved
前端