js函数柯里化-面试手写版
概念
用我自己的话来总结一下,函数柯里化的意思就是你可以一次传很多参数给curry函数,也可以分多次传递,curry函数每次都会返回一个函数去处理剩下的参数,一直到返回最后的结果。
实例
这里还是举几个例子来说明一下:
柯里化求和函数
// 普通方式var add1 = function(a, b, c){return a + b + c;}// 柯里化var add2 = function(a) {return function(b) {return function(c) {return a + b + c;}}}
这里每次传入参数都会返回一个新的函数,这样一直执行到最后一次返回a+b+c的值。
但是这种实现还是有问题的,这里只有三个参数,如果哪天产品经理告诉我们需要改成100次?我们就重新写100次?这很明显不符合开闭原则,所以我们需要对函数进行一次修改。
var add = function() {var _args = [];return function() {if(arguments.length === 0) {return _args.reduce(function(a, b) {return a + b;})}[].push.apply(_args, arguments);return arguments.callee;}
}
var sum = add();
sum(100, 200)(300);
sum(400);
sum(); // 1000
我们通过判断下一次是否传进来参数来决定函数是否运行,如果继续传进了参数,那我们继续把参数都保存起来,等运行的时候全部一次性运行,这样我们就初步完成了一个柯里化的函数。
通用柯里化函数
这里只是一个求和的函数,如果换成求乘积呢?我们是不是又需要重新写一遍?仔细观察一下我们的add函数,如果我们将if里面的代码换成一个函数执行代码,是不是就可以变成一个通用函数了?
var curry = function(fn) {var _args = [];return function() {if(arguments.length === 0) {return fn.apply(fn, _args);}[].push.apply(_args, arguments);return arguments.callee;}
}
var multi = function() {return [].reduce.call(arguments, function(a, b) {return a + b;})
}
var add = curry(multi);
add(100, 200, 300)(400);
add(1000);
add(); // 2000
在之前的方法上面,我们进行了扩展,这样我们就已经实现了一个比较通用的柯里化函数了。
也许你想问,我不想每次都使用那个丑陋的括号结尾怎么办?
var curry = function(fn) {var len = fn.length,args = [];return function() {Array.prototype.push.apply(args, arguments)var argsLen = args.length;if(argsLen < len) {return arguments.callee;}return fn.apply(fn, args);}
}
var add = function(a, b, c) {return a + b + c;
}var adder = curry(add)
adder(1)(2)(3)
这里根据函数fn的参数数量进行判断,直到传入的数量等于fn函数需要的参数数量才会返回fn函数的最终运行结果,和上面那种方法原理其实是一样的,但是这两种方式都太依赖参数数量了。
我在简书还看到别人的另一种递归实现方法,其实实现思路和我的差不多吧。
// 简单实现,参数只能从右到左传递
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);}// 参数收集完毕,则执行funcreturn func.apply(this, _args);}
}
这里是对参数个数进行了计算,如果需要无限参数怎么办?比如下面这种场景。
add(1)(2)(3)(2);
add(1, 2, 3, 4, 5);
这里主要有一个知识点,那就是函数的隐式转换,涉及到toString和valueOf两个方法,如果直接对函数进行计算,那么会先把函数转换为字符串,之后再参与到计算中,利用这两个方法我们可以对函数进行修改。
var num = function() {
}
num.toString = num.valueOf = function() {return 10;
}
var anonymousNum = (function() { // 10return num;
}())
经过修改,我们的函数最终版是这样的。参考 前端进阶面试题详细解答
var curry = function(fn) {var func = function() {var _args = [].slice.call(arguments, 0);var func1 = function() {[].push.apply(_args, arguments)return func1;}func1.toString = func1.valueOf = function() {return fn.apply(fn, _args);}return func1;}return func;
}
var add = function() {return [].reduce.call(arguments, function(a, b) {return a + b;})
}var adder = curry(add)
adder(1)(2)(3)
那么我们说了那么多,柯里化究竟有什么用呢?
预加载
在很多场景下,我们需要的函数参数很可能有一部分一样,这个时候再重复写就比较浪费了,我们提前加载好一部分参数,再传入剩下的参数,这里主要是利用了闭包的特性,通过闭包可以保持着原有的作用域。
var match = curry(function(what, str) {return str.match(what);
});match(/\s+/g, "hello world");
// [ ' ' ]match(/\s+/g)("hello world");
// [ ' ' ]var hasSpaces = match(/\s+/g);
// function(x) { return x.match(/\s+/g) }hasSpaces("hello world");
// [ ' ' ]hasSpaces("spaceless");
// null
上面例子中,使用hasSpaces函数来保存正则表达式规则,这样可以有效的实现参数的复用。
动态创建函数
这个其实也是一种惰性函数的思想,我们可以提前执行判断条件,通过闭包将其保存在有效的作用域中,来看一种我们平时写代码常见的场景。
var addEvent = function(el, type, fn, capture) {if (window.addEventListener) {el.addEventListener(type, function(e) {fn.call(el, e);}, capture);} else if (window.attachEvent) {el.attachEvent("on" + type, function(e) {fn.call(el, e);});} };
在这个例子中,我们每次调用addEvent的时候都会重新进行if语句进行判断,但是实际上浏览器的条件不可能会变化,你判断一次和判断N次结果都是一样的,所以这个可以将判断条件提前加载。
var addEventHandler = function(){if (window.addEventListener) {return function(el, sType, fn, capture) {el.addEventListener(sType, function(e) {fn.call(el, e);}, (capture));};} else if (window.attachEvent) {return function(el, sType, fn, capture) {el.attachEvent("on" + sType, function(e) {fn.call(el, e);});};}
}
var addEvent = addEventHandler();
addEvent(document.body, "click", function() {}, false);
addEvent(document.getElementById("test"), "click", function() {}, false);
但是这样做还是有一种缺点,因为我们无法判断程序中是否使用了这个方法,但是依然不得不在文件顶部定义一下addEvent,这样其实浪费了资源,这里有一种更好的解决方法。
var addEvent = function(el, sType, fn, capture){if (window.addEventListener) {addEvent = function(el, sType, fn, capture) {el.addEventListener(sType, function(e) {fn.call(el, e);}, (capture));};} else if (window.attachEvent) {addEvent = function(el, sType, fn, capture) {el.attachEvent("on" + sType, function(e) {fn.call(el, e);});};}
}
在addEvent函数里面对其重新赋值,这样既解决了每次运行都要判断的问题,又解决了必须在作用域顶部执行一次造成浪费的问题。
React
在回家的路上我一直在想函数柯里化是不是可以扩展到更多场景,我想把函数换成react组件试试?我想到了高阶组件和redux的connect,这两个确实是将柯里化思想用到react里面的体现。我们想一想,如果把上面例子里面的函数换成组件,参数换成高阶函数呢?
var curry = function(fn) {var func = function() {var _args = [].slice.call(arguments, 0);var func1 = function() {[].push.apply(_args, arguments)return func1;}func1.toString = func1.valueOf = function() {return fn.apply(fn, _args);}return func1;}return func;
}var hoc = function(WrappedComponent) {return function() {var len = arguments.length;var NewComponent = WrappedComponent;for (var i = 0; i < len; i++) {NewComponent = arguments[i](NewComponent)}return NewComponent;}
}
var MyComponent = hoc(PageList);
curry(MyComponent)(addStyle)(addLoading)
这个例子是对原来的PageList组件进行了扩展,给PageList加了样式和loading的功能,如果想加其他功能,可以继续在上面扩展(注意addStyle和addLoading都是高阶组件),但是写法真的很糟糕,一点都不coooooool,我们可以使用compose方法,underscore和loadsh这些库中已经提供了。
var enhance = compose(addLoading, addStyle);
enhance(MyComponent)
总结
其实关于柯里化的运用核心还是对函数闭包的灵活运用,深刻理解闭包和作用域后就可以写出很多灵活巧妙的方法。
相关文章:
js函数柯里化-面试手写版
概念 用我自己的话来总结一下,函数柯里化的意思就是你可以一次传很多参数给curry函数,也可以分多次传递,curry函数每次都会返回一个函数去处理剩下的参数,一直到返回最后的结果。 实例 这里还是举几个例子来说明一下࿱…...
【学习笔记】深入理解JVM之类加载机制
【学习笔记】深入理解JVM之类加载机制 以后基本上都在语雀上面更新,大家有兴趣可以看看嗷! 首发地址: 知识库 文章流程图: 1、概述 首先我们先来看看一个 Class 文件所需要经过的一个流程图: 而我们今天要重点需讲的…...
驾驭云端之风1——Spring Cloud微服务架构实践指南
本博客纯属个人总结,非原创。喜欢技术交流的,可关注博主,武汉有后端开发群,可支持内推,了解武汉行情等。 前沿 优惠卷平台项目的整体功能和模块,以及每个功能点的技术选型和背后的依据。 搭建一个简化版的…...
【计算机网络基础】
计算机网络基础网络的基本概念网络互联网IP地址MAC地址网络协议网络分层模型网络应用程序的通信流程网络的基本概念 网络 网络是由若干结点和链接这些结点的链路组成,网络中的结点可以是计算机,交换机,路由器等设备 网络设备:交…...
grep与nm命令的应用
相关知识拓展 Linux中grep的命令使用 在Linux中,grep可用于shell脚本,因为grep通过返回一个状态值来说明搜索状态,如果模板搜索成功,则返回0,如果搜索不成功,则返回1,如果搜索的文件不存在&…...
【linux】软硬链接
在linux中在磁盘中定位文件并不是根据文件名而是根据文件的inode,一个文件对应一个inode但是一个inode可以对应多个文件。硬链接硬链接是通过索引节点进行的链接。在Linux中,多个文件指向同一个索引节点是允许的,像这样的链接就是硬链接。硬链…...
骨传导蓝牙耳机排行,盘点几款性能不错的骨传导耳机
随着蓝牙耳机的普及,骨传导耳机也越来越受到欢迎,很多人也都开始在了解并尝试骨传导耳机。相比于其他类型耳机,在舒适度、安全方面有一定优势。尤其是在户外运动时,或者长时间佩戴运动时,使用骨传导耳机可以避免耳朵因…...
ARM中的寄存器
ARM工作模式 ARM有8个基本的工作模式 User 非特权模式,一般在执行上层的应用程序时ARM处于该模式FIQ 当一个高优先级中断产生后ARM将进入这种模式IRQ 当一个低优先级中断产生后ARM将进入这种模式SVC 当复位或执行软中断指令后ARM将进入这种模式Abort 当产生存取异常…...
git操作修改历史版本指定tag标签的代码,并发布新标签
场景: 当项目已经迭代多个版本之后,突然发现旧版本0.0.1出现了紧急bug,需要及时处理; 如果直接用新版本替换上去是存在极大隐患的,且时间来不及; 所以需要直接在0.0.1版本的基础上去修复bug,然…...
SpringMVC——响应处理(1)【包含源码分析】
Controller public class JsonReturnController {ResponseBodyGetMapping("/getPet")public Pet getPet(){Pet petnew Pet();pet.setAge(5);pet.setName("lily");return pet;} }项目启动后 浏览器输入 http://localhost:8080/getPet 。 debug DispatcherS…...
Normalization
1、BN(Batch Normalization) 深度网络参数训练时内部存在协方差偏移(Internal Covariate Shift)现 象:深度网络内部数据分布在训练过程中发生变化的现象。训练深度网络时,神经网络隐层参数更新会导致网络输…...
27K测试老鸟分享自己6年面试心得,四种公司、四种问题…
这里总结了下自己今年的面试情况 先说一下自己的个人情况,普通二本计算机专业毕业,懂python,会写脚本,会selenium,会性能。趁着金三银四跳槽季,面试字节跳动测试岗技术面都已经过了,本来以为是…...
中小企业数字化自动化转型的方法
自动化是我们国内未来的趋势。智能制造的实现主要依托两个基础能力,一个是工业制造技术,另一个就是工业互联网。而自动化是工业制造技术的重要组成部分,是高度智能制造装备的核心部分,与承接着制造单元与工业互联网这两大核心。懂…...
利用GPT-3 Fine-tunes训练专属语言模型
利用GPT-3 Fine-tunes训练专属语言模型 文章目录什么是模型微调(fine-tuning)?为什么需要模型微调?微调 vs 重新训练微调 vs 提示设计训练专属模型数据准备清洗数据构建模型微调模型评估模型部署模型总结什么是模型微调࿰…...
kubeadm方式安装k8s高可用集群(版本1.26x)
K8S官网:https://kubernetes.io/docs/setup/ 高可用Kubernetes集群规划 配置备注系统版本CentOS 7.9Docker版本20.10.xPod网段172.16.0.0/12Service网段10.103.10.0/16 主机IP说明k8s-master01 ~ 03192.168.77.101 ~ 103master节点 * 3k8s-master-lb192.168.77.2…...
分享5款堪称神器的免费软件,建议先收藏再下载
转眼间新年已经过去一个月了,最近陆陆续续收到好多小伙伴的咨询,这边也是抓紧整理出几个好用的软件,希望可以帮到大家。 1.电脑安全管家——火绒 火绒是一款电脑安全软件,病毒库更新及时,界面清晰干净,没…...
【项目实战】从0开始入门JDK源码 - LinkedList源码
一、源码位置 一般来说IDEA配置好JDK以后 ,JDK的源码其实也配置好了,本文是基于JDK1.8的源码说明 rt - java - util - LinkedList 二、 继承关系图 LinkedList public class LinkedList<E>extends AbstractSequentialList<E>implements...
Polygon zkEVM的gas定价
1. 引言 所有的zkEVM都存在一个有趣的问题: 如何给gas定价? 在Ethereum Virtual Machine (EVM)中,gas通过为每个计算设置economic fee,来保持网络安全。恶意行为,如拒绝服务(DoS)攻击&#x…...
stl中的智能指针类详解
C98/03的尝试——std::auto_ptr C11标准废弃了std::auto_ptr(在C17标准中被移除),取而代之的是std::unique_ptr, std::auto_ptr容易让人误用的地…...
Linux 阻塞和非阻塞 IO 实验
目录 一、阻塞和非阻塞简介 1、IO 概念 2、阻塞与非阻塞 二、等待队列 1、等待队列头 2、等待队列项 3、将队列项添加/移除等待队列头 4、等待唤醒 5、等待事件 三、轮询 1、应用程序的非阻塞函数 2、Linux 驱动下的 poll 操作函数 四、阻塞IO之等待事件唤醒 添加…...
你要的react+ts最佳实践指南
本文根据日常开发实践,参考优秀文章、文档,来说说 TypeScript 是如何较优雅的融入 React 项目的。 温馨提示:日常开发中已全面拥抱函数式组件和 React Hooks,class 类组件的写法这里不提及。 前沿 以前有 JSX 语法,…...
软件测试人员会被替代吗?IT行业哪个方向的前景最好?字节12年测开是这样说的
互联网测试从业12年,前来作答。 逻辑上来说,软件工程最初始只需要两个岗位,一个是产品经理。,一个是研发(开发),剩余的 所有岗位都是由他们衍生而来的。 第三个岗位大概率就是测试,…...
十六、vue3.0之富文本编辑器的选择
在工作过程中我们会遇到很多的时候会使用到富文本编辑器,市场上流行的也是各种各样的,那么究竟如何选择呢,今天就给大家讲讲有哪一些,方便大家的选择。 一、TinyMCE TinyMCE 是富文本编辑器领域的头部玩家之一,主流富文本编辑器,功能非常全,你需要的大多数功能它都支持…...
kafka(一) 的架构,各概念
Kafka架构 Kafak 总体架构图中包含多个概念: (1)ZooKeeper:Zookeeper负责保存broker集群元数据,并对控制器进行选举等操作。 (2)Producer: 生产者负责创建消息,将消息发…...
【ts的常用类型】
ts的常用类型前言安装ts常见类型原始类型 、数组、 any变量上的类型注解函数对象类型联合类型类型别名接口接口和类型别名的对比前言 typescript中为了使编写的代码更规范,更有利于维护,增加了类型校验,安装 安装 typescript npm i typescr…...
Hyper-V与安卓模拟器不共存
一是某些新的模拟器已经开始使用新接口开发,支持了共存,安装这种新的安卓模拟器即可。 对于不支持共存的模拟器,只得增加一个windows开机后的系统选项,如果需要切换这两种不同选项使用系统,每次切换都需要重启windows系…...
【图像分类】卷积神经网络之ZFNet网络模型结构详解
写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 1. 前言 由于AlexNet的提出,大型卷积网络开始变得流行起来,但是人们对于网络究竟为什么能表现的这么好,以及怎…...
亿级高并发电商项目-- 实战篇 --万达商城项目 十三(编写购物车、优化修改商品、下架商品方法、购物车模块监听修改商品、删除商品消息)
👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者 📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 Ǵ…...
springboot 虚拟线程demo
jd19支持虚拟线程,虚拟线程是轻量级的线程,它们不与操作系统线程绑定,而是由 JVM 来管理。它们适用于“每个请求一个线程”的编程风格,同时没有操作系统线程的限制。我们能够创建数以百万计的虚拟线程而不会影响吞吐。 做个 spri…...
CTFer成长之路之逻辑漏洞
逻辑漏洞CTF 访问url: http://1b43ac78-61f7-4b3c-9ab7-d7e131e7da80.node3.buuoj.cn/ 登录页面用随意用户名密码登录 访问url: http://1b43ac78-61f7-4b3c-9ab7-d7e131e7da80.node3.buuoj.cn/user.php 登陆后有商品列表,共三个商品,点击购买flag 钱…...
洛阳做网站公司汉狮价格/痘痘怎么去除有效果
随着Internet与网络的迅速发展并逐步走进人们的家庭,使消费电子、计算机、通信(3C)一体化趋势日趋明显,嵌入式系统也成为研究与应用的热点。嵌入式实时Linux操作系统以价格低廉、功能强大又易于移植而正在被广泛采用,成为新兴的力量ÿ…...
搜索企业信息的网站/百度首页排名优化公司
Maven环境配置 下载mavenhttp://mirror.bit.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz 上传到linux /usr/local/src目录下 解压 tar zxvf apache-maven-3.3.9-bin.tar.gz 移动目录mvapache-maven-3.3.9 /usr/local/ 进入目录cd/usr/loca…...
马鞍山专业网站制作公司/南京seo代理
Scala中的特质类似于Java中的接口,不过trait中可以有实现的方法,也可以有属性.如果是一个trait可以用 extends 剩下的就可以用with trait Person{def eat(str:String){println(str)} } trait Worker{def work{println("working")} } class Student extends Worker w…...
律师做网站推广有用吗/收录网站排名
题目链接:http://codeforces.com/gym/101778/problem/B Codeforces上的一篇题解:http://codeforces.com/blog/entry/58982 题意:给出n和a,让找一个n个数的正整数集合使得这个集合中的元素累加和的平均值等于a,基于此求…...
找别人做网站 自己管理/app开发公司排行榜
周五下午面试4399,4399的前台妹子确实给力,刚进去的时候应该赶上校招吧,比较多的应届生来面试,前台妹子给了我一张面试申请表给我填了,填完之后,过了段时间正式进入面试,面试的过程整个比较融洽…...
政府部门做网站/互联网精准营销
1.开启root登录(1)删除/etc/ftpusers里的root(2)在proftpd.conf里添加如下配置:RootLogin on 2.配置匿名登录,对protfpd.conf做如下配置: <Anonymous /app> #匿名登录后进行ftp上传下…...