实现一个通用的函数柯里化的函数
创始人
2024-02-10 10:34:54

本文目的

这篇文章会一步一步带你实现一个通用的函数柯里化的函数,即使你完全不懂柯里化这个概念也没关系,我会先介绍柯里化函数的概念,然后以实现一个完整的判断变量类型的函数的例子来让你直观体验函数柯里化,然后再实现一个通用的函数柯里化函数,所以这篇文章至少能带你掌握这两点:

  1. 实现一个通用的函数柯里化函数
  2. 借助柯里化实现一个完整的判断变量类型的函数

总体目录

  1. 函数柯里化的概念
  2. 柯里化函数初体验
  3. 实现一个通用的函数柯里化函数
  4. 小结

函数柯里化概念

首先什么是函数柯里化?得先把概念搞清楚,函数柯里化就是把多个参数的传入转化成 n 个函数依次进行传参。(注意,柯里化一般都要求参数个数是确定的,并且是一个一个传)

觉得有点抽象没关系,下面以一个实现判断变量类型函数的例子来体验下柯里化函数。

首先我们先来思考一下可以用什么方法来判断类型?

  1. 我们可以用typeof来判断类型,但是用 typeof 有缺陷,它无法进一步判断对象,并且typeof null的值是 object,所以 typeof 我们一般用于判断基础类型;
  2. 除了 typeof 之外,我们可以用 instanceof来判断谁是谁的实例;
  3. Object.prototype.toString.call 来判断具体类型,返回的是一个字符串;
  4. 通过constructor

那我们就开写,通过Object.prototype.toString.call,实现一个基础版本:

	function isType(val, type) {return Object.prototype.toString.call(val) === `[object ${type}]`}console.log(isType(123, 'String'));   // falseconsole.log(isType(123, 'Number'));   // true

但是这么做需要用户手动去写'String'这些常量,有点风险,你怎么保证用户不会一不小心把'String'写成'Stirng'呢?

柯里化函数初体验

怎么解决上面说的那个问题?那我就希望这个isType()函数能更具体些,形如isString() isNumber()之类的。

那么此时柯里化就要登场了,以isString为例,来直观感受一下柯里化:原来isType函数是传了两个参数,那么我就要把isString写成两次调用的形式:

	function isString(type) {return function(val) {return Object.prototype.toString.call(val) === `[object ${type}]`}}let myIsString = isString('String')console.log(myIsString(123));       // falseconsole.log(myIsString('123'));     // true

isString('String')是第一次调用传参,调用完返回一个函数赋值给了myIsString,随后myIsString()第二次调用传参123,执行返回结果,这就是函数柯里化的感觉。

如此一来,我们就可以让用户直接通过 myIsString()来判断是否是字符串了。

要完整判断类型,只判断 String 肯定不够,那接下来就要实现isNumber isNull等等,一个一个写? 很麻烦,能不能有个通用的柯里化函数来帮我们完成这件事呢?我只要传String``Number这些参数就好,而不必自己去手动实现isNumber等等函数。

下面就来实现这个通用的柯里化函数,然后再解决上面说的这个类型判断的问题。

实现这个通用柯里化函数的思路:因为我们要把函数fn(fn就是我们希望将其柯里化的函数)的传参,改为一个一个传,并且传一个参数,就返回一个函数,所以我们每次传参的时候我们都要将参数进行记录,当记录的参数个数小于原本函数fn参数的个数时,就返回一个新的函数,如果等于,就真正执行这个函数fn。

看代码:

	function curring(fn) {const inner = (args = []) => {return args.length >= fn.length ? fn(...args) : (...userArgs) => inner([...args, ...userArgs]) }return inner()}// testlet sum1 = curring(sum)let sum2 = sum1(1)let sum3 = sum2(2,3)let result = sum3(4)console.log(result);    // 10

inner 主要是用来保存传入的参数,存在 args 中,userArgs代表每次调用新传的参数,如果累积的参数和原函数fn的个数一样,就执行fn,否则就把参数继续存到 args 中。这里面有递归的思想,不熟悉递归的多看多写几遍,好好体会。

上面的这个curring就是一个通用的实现函数柯里化的函数了,下面我们就用这个通用的柯里化函数来实现我们还未解决的类型判断问题。

	function isType(type, val) {return Object.prototype.toString.call(val) === `[object ${type}]`}let util = {};let typeArr = ['String', 'Number', 'Boolean', 'Null', 'Undefined', 'Symbol', 'Function', 'Array', 'Object', 'Date']typeArr .forEach(type => {util[`is${type}`] = curring(isType)(type)});// testconsole.log(util.isNumber('123'))  // falseconsole.log(util.isNull(null))  // true

小结

第一遍看不懂没关系,下面这段核心代码,一定要结合文章多看几遍,多敲几遍,仔细体会它是如何把 fn 这个函数进行柯里化的。

	function curring(fn) {const inner = (args = []) => {return args.length >= fn.length ? fn(...args) : (...userArgs) => inner([...args, ...userArgs]) }return inner()}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...