做陪玩平台,进一步了解JavaScript函数柯里化

做陪玩平台,进一步了解JavaScript函数柯里化

一、定义:

柯里化(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.zhihu.com', '/saandsa');
const url3 = getUrl('https://', 'www.segmentfault.com', '/hasak');

console.log(url1, url2, url3)
复制代码

 

每次调用getUrl参数的时候都要重复的传入参数’https://'。

柯里化封装之后:

复制代码
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.zhihu.com', '/saandsa');
const url3 = url_curry('www.segmentfault.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 addEvent = (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)
            })
        }
    }
})()

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 curryWeight(fn) {
    let _fishWeight = [];
    return function () {
        if (arguments.length === 0) {
            return fn.apply(null, _fishWeight);
        } else {
            _fishWeight = _fishWeight.concat([...arguments]);
        }
    }
}

function sortWeight() {
    return [...arguments].sort((a, b) => a - b);
}

const _addWeight = curryWeight(sortWeight);
_addWeight(6.5);
_addWeight(1.2);
_addWeight(2.3);
_addWeight(2.5);
console.log(_addWeight())
复制代码

 

很简单,只需要修改addWeight函数即可。

三、柯里化函数的实现

Done1:
step1:实现一个函数add(1)(2)

function add(num){
    let sum = num;
    return function(num){
      return sum+num
    }
  }
console.log(add(1)(2));

 

step2:实现add(1)(2)(3)呢?
不能一直无限嵌套,所以,返回一个函数名,在函数内部判断是否还有参数传进来

复制代码
 function add(num){
    let sum =num;
    return adds = function (num1){
     
      if(num1) {
        sum = sum+num1;
        return adds
      }
      return sum
    }
  }
console.log(add(1)(2)(3)());
复制代码

 

step3:如果实现一个add(1,2,3)(2,1)(3)(2,1,3)呢?
因为现在每次调用传入的参数是不定长的。所以并不能直接把形参给写死。

复制代码
function add() {
    // 收集参数
    let params = [...arguments];

    const getParams = function () {
        // 当传入的参数为空时,对参数数组遍历求和。
        if ([...arguments].length === 0) return params.reduce((pre, next) => pre + next, 0);
        // 收集参数
        params.push(...arguments);
        return getParams;
    }

    // 重复调用,直到传入参数为空
    return getParams;
}
let a = add(1, 2)(2, 1)(3)();
console.log(a);
复制代码

 

Done2:

复制代码
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(+result);
复制代码

 

有关隐式转换
在JavaScript中,toString()方法和valueOf()方法都可以被改写,如果被用以操作JavaScript解析器就会自动调用。所以在柯里化函数中最后重写toString恰好能满足在收集完所有参数后再去执行。

四、柯里化总结

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

优点:
柯里化之后,我们没有丢失任何参数:log 依然可以被正常调用;
我们可以轻松地生成偏函数,例如用于生成今天的日志的偏函数;
入口单一;
易于测试和复用。
缺点:
函数嵌套多;
占内存,有可能导致内存泄漏(因为本质是配合闭包实现的);
效率差(因为使用递归);
变量存取慢,访问性很差(因为使用了arguments);
应用场景:
减少重复传递不变的部分参数;
将柯里化后的callback参数传递给其他函数。

以上就是做陪玩平台,进一步了解JavaScript函数柯里化, 更多内容欢迎关注之后的文章

posted @   云豹科技-苏凌霄  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2023-02-22 直播带货源码,日期时间选择器 选择范围限制
2023-02-22 直播系统搭建,vue之computed带参数如何接收
2023-02-22 直播平台软件开发,前端实现登录拼图验证
2022-02-22 一对一聊天源码,验证码选择输入字母验证
2022-02-22 短视频app开发,三种图片并排展示的方式
2022-02-22 直播平台源码,视频抽帧作为图片保存
点击右上角即可分享
微信分享提示