一文带你了解JavaScript函数柯里化

 更新时间:2023年02月01日 09:36:44   作者:起风了  
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。本文将通过一些示例带大家深入了解JS的函数柯里化,需要的可以参考一下

一、定义

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

通过一个简单的例子解释一下

function add(a, b) {
    return a + b
}

add(1, 2); // 3

将函数add转化为柯里化函数_add

function _add(a){
    return function(b){
        return a + b
    }
}

_add(1)(2);  // 3

函数add和函数_add是等价的。

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

实际上就是把add函数的a,b两个参数变成了先用一个函数接收a然后返回一个函数去处理b参数。

现在思路应该就比较清晰了:只传递给函数一部分参数来调用,让它返回一个函数去处理剩下的参数。

二、柯里化函数的作用

1、参数复用

案例:拼接地址

按照普通思路去拼接一个地址

// 拼接地址
function getUrl(protocol, hostname, pathname) {
    return `${protocol}${hostname}${pathname}`;
}

const url1 = getUrl('https://', 'www.baidu.com', '/hasa');
const url2 = getUrl('https://', 'www.baidu.com', '/saandsa');
const url3 = getUrl('https://', 'www.baidu.com', '/hasak');

console.log(url1, url2, url3)

每次调用getUrl参数的时候都要重复的传入参数'https://', 'www.baidu.com'。

柯里化封装之后:

function curry(protocol) {
    return function (hostname, pathname) {
        return `${protocol}${hostname}${pathname}`;
    }
}

const url_curry = curry('https://');

const url1 = url_curry('www.baidu.com', '/hasa');
const url2 = url_curry('www.baidu.com', '/saandsa');
const url3 = url_curry('www.baidu.com', '/hasak');

console.log(url1, url2, url3)

很明显,经过柯里化封装之后,之后再进行地址拼接的时候,减少了参数个数,降低了代码重复率。

2、提前确认

案例:兼容IE浏览器事件的监听方法(IE is dead)

传统的方法:

/*
* @param    element       Object      DOM元素对象
* @param    type          String      事件类型
* @param    listener      Function    事件处理函数
* @param    useCapture    Boolean     是否捕获
*/
var addEvent = function (element, type, listener, useCapture) {
    if (window.addEventListener) {
        element.addEventListener(type, function (e) {
            listener.call(element, e)
        }, useCapture)
    } else {
        element.attachEvent('on' + type, function (e) {
            listener.call(element, e)
        })
    }
}

缺陷就是,每次对DOM元素进行事件绑定时都需要重新进行判断,其实对于事件监听网页一发布浏览器已经确定了,就可以知晓浏览器到底是需要哪一种监听方式。所以我们可以让判断只执行一次。

柯里化封装之后:

var curEvent = (function () {
    if (window.addEventListener) {
        return function (element, type, listener, useCapture) { // return funtion
            element.addEventListener(type, function () {
                listener.call(element)
            }, useCapture)
        }
    } else {
        return function (element, type, listener) {
            element.attachEvent('on' + type, function () {
                listener.call(element)
            })
        }
    }
})

var addEvent = curEvent();
addEvent(element, "click", listener)

立即执行函数,在触发多次事件也依旧只会触发一次if条件判断。

3、延迟执行

案例:钓鱼统计重量

传统的方法:

let fishWeight = 0;
const addWeight = function(weight) {
    fishWeight += weight;
};

addWeight(2.3);
addWeight(6.5);
addWeight(1.2);
addWeight(2.5);

console.log(fishWeight);

每次执行addWeight方法时,都做了一次鱼的体重的加和。

柯里化封装后:

function curryWeight(fn) {
    let _fishWeight = [];
    return function () {
        if (arguments.length === 0) {
            return fn.apply(null, _fishWeight);
        } else {
            _fishWeight = _fishWeight.concat([...arguments]);
        }
    }
}

function addWeight() {
    let fishWeight = 0;
    for (let i = 0, len = arguments.length; i < len; i++) {
        fishWeight += arguments[i];
    }
    return fishWeight;
}

const _addWeight = curryWeight(addWeight);
_addWeight(6.5);
_addWeight(1.2);
_addWeight(2.3);
_addWeight(2.5);
console.log(_addWeight())

在执行_addWeight方法时,并没有做鱼的体重的加和,之后在最后一次执行_addWeight()时,才做了加和。做到了延迟执行addWeight方法的效果。

三、柯里化函数的实现

function curry() {
    let args = [...arguments];
    let inner = function () {
        args.push(...arguments);
        return inner;
    }
    
    // 核心内容:隐式转换,调用了内部的toString
    inner.toString = function () {
        return args.reduce(function (pre, next) {
            return pre + next;
        })
    }
    return inner;
}

const result = curry(1)(2);
console.log(Number(result));

四、柯里化总结

函数的柯里化,需要依赖参数以及递归,通过拆分参数的方式,来调用一个多参数的函数方法,以达到减少代码冗余,增加可读性的目的。

性能方面:

  • 存取arguments对象通常要比存取命名参数要慢一点;
  • 一些老版本的浏览器在arguments.length的实现上是相当慢的;
  • 使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点;
  • 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上。

应用场景:

  • 减少重复传递不变的部分参数;
  • 将柯里化后的callback参数传递给其他函数。

到此这篇关于一文带你了解JavaScript函数柯里化的文章就介绍到这了,更多相关JavaScript函数柯里化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Bootstrap输入框组件使用详解

    Bootstrap输入框组件使用详解

    这篇文章主要为大家详细介绍了Bootstrap输入框组件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • Javascript实现秒表倒计时功能

    Javascript实现秒表倒计时功能

    这篇文章主要为大家详细介绍了Javascript实现秒表倒计时功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-11-11
  • js 获取元素下面所有li的两种方法

    js 获取元素下面所有li的两种方法

    这篇文章主要介绍了js如何获取元素下面所有的li,有哪些方法,需要的朋友可以参考下
    2014-04-04
  • JavaScript实现的仿新浪微博原生态输入字数即时检查功能【兼容IE6】

    JavaScript实现的仿新浪微博原生态输入字数即时检查功能【兼容IE6】

    这篇文章主要介绍了JavaScript实现的仿新浪微博原生态输入字数即时检查功能,涉及javascript事件响应及字符串的遍历、转换、判断等相关操作技巧,需要的朋友可以参考下
    2017-09-09
  • JavaScript实现数值自动增加动画

    JavaScript实现数值自动增加动画

    这篇文章主要为大家详细介绍了JavaScript实现数值自动增加动画,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • JavaScript 对象模型 执行模型

    JavaScript 对象模型 执行模型

    简单数值类型: 有Undefined, Null, Boolean, Number和String。注意,描述中的英文单词在这里仅指数据类型的名称,并不特指JS的全局对象N an, Boolean, Number, String等,它们在概念上的区别是比较大的。
    2010-10-10
  • Js图片点击切换轮播实现代码

    Js图片点击切换轮播实现代码

    这篇文章主要介绍了Js图片点击切换轮播实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 如何给JS中的数组开头添加元素

    如何给JS中的数组开头添加元素

    这篇文章主要介绍了如何给JS中的数组开头添加元素,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • JavaScript defineProperty如何实现属性劫持

    JavaScript defineProperty如何实现属性劫持

    双向数据绑定的核心方法,主要是做数据劫持操作(监控数据变化),下面这篇文章主要给大家介绍了关于JavaScript defineProperty如何实现属性劫持的相关资料,需要的朋友可以参考下
    2021-07-07
  • js+html制作简单验证码

    js+html制作简单验证码

    这篇文章主要为大家详细介绍了js结合html制作简单验证码的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02

最新评论