函数柯里化

文章介绍了函数柯里化的概念,即把接受多个参数的函数转换成接受单一参数的函数,从而实现参数复用和提前确认的效果。通过示例展示了柯里化如何减少代码冗余,提高代码复用性,特别是在正则验证和事件绑定中的应用。同时,提供了一个通用的柯里化函数实现,并给出了相关的面试题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 什么是函数柯里化?

固定某些参数,将接受多个参数的函数变换成接受单一参数的函数。

2. 应用

2.1 参数复用

例1:在一款游戏app下,判断用户的年龄是否大于18

let limit=(levle,age)=>{
  return age>level
}
// 调用时:
limit(18,19)
limit(18,17)
limit(18,16)
limit(18,20)

···
柯里化后:

let limit=(level)=>{
 return (age)=>{
   age>level
  }
}
调用时:
let limitChina=limit(18)
limitChina(21)
limitChina(17)

let limitAmerica=limit(20)
limitChina(21)
limitChina(17)

柯里化前,每次都重复传值18,在比较复杂的业务下,传参比较多,逻辑比较复杂时,会造成代码冗余。

柯里化后,固定了某些参数,得到一个子函数,该子函数可以在leval=18业务下所有的页面使用。且调用多次不用传递重复的参数。如果业务拓展到了国外,只需要传递不同的level,可以得到一个新的子函数,可以在这套业务下的所有页面使用。

好处: 固定了参数,形成子函数,提高复用。避免了参数过多的代码冗余。
例2:利用柯里化封装正则验证

// 正常正则验证字符串 reg.test(txt)

// 函数封装后
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

// Currying后
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.2 提前确认
/**
 * 
 * @param {要绑定事件的 DOM 元素} element 
 * @param {绑定什么事件} event 
 * @param {事件处理函数} handler 
 */
var on = function (element, event, handler) {
    if (document.addEventListener) {
        if (element && event && handler) {
            element.addEventListener(event, handler, false);
        }
    } else {
        if (element && event && handler) {
            element.attachEvent('on' + event, handler);
        }
    }
}

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


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(){})

//换一种写法可能比较好理解一点,上面就是把 isSupport 这个参数给先确定下来了
var on = function (isSupport, element, event, handler) {
    isSupport = isSupport || document.addEventListener;
    if (isSupport) {
        return element.addEventListener(event, handler, false);
    } else {
        return element.attachEvent('on' + event, handler);
    }
}
on(true, div, 'click', function(){})
on(true, div, 'click', function(){})
on(true, div, 'click', function(){})

相对于第一种方法,第二种方法自执行然后返回一个新的函数,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断。

3. 柯里化面试题

1.实现一个通用的柯里化函数,参数不变,可通过任意次数传入

function curry() {
    var fn = arguments[0]; // 获取要执行的函数
    var args = [].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;
}

验证:

// 测试 1
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

// 测试 2
function check(reg, txt) {
    return reg.test(txt)
}
var hasNumber = curry(check)(/\d+/g);
console.log(hasNumber('test1'));// true

2.实现一个add,使得计算结果满足如下预期
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

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

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

    // 这个是最后输出的时候被调用的,return 后面如果是函数体,
    // 为了输出函数体字符串会自动调用 toString 方法
    // 利用 toString 隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }

    // 这个 return 是第一次调用的时候返回上面的函数体,
    // 这样后面所有的括号再执行的时候就是执行 _adder 函数体
    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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值