沧州市任丘建设局网站,烟台seo网站推广,网站内容与目录结构,兄弟们有没有没封的网站彻底弄懂js函数柯里化 1、前言2、什么是柯里化3、实现原理4、应用场景4.1 参数复用4.2 遍历数组 1、前言
函数柯里化(Currying)在JavaScript中总感觉属于一种不温不火的存在#xff0c;甚至有些开发者在提起柯里化时#xff0c;竟然会有点生疏不懂。其实不然#xff0c;对于… 彻底弄懂js函数柯里化 1、前言2、什么是柯里化3、实现原理4、应用场景4.1 参数复用4.2 遍历数组 1、前言
函数柯里化(Currying)在JavaScript中总感觉属于一种不温不火的存在甚至有些开发者在提起柯里化时竟然会有点生疏不懂。其实不然对于它们的概念可能在日常开发中不太提到但是它们的思想和用法却在前端开发中经常借鉴和使用它可以帮助我们写出更加优雅、灵活的代码。本文将介绍柯里化概念、实现原理和应用场景希望对大家能有所帮助
2、什么是柯里化
函数柯里化指的是将能够接收多个参数的函数转化为接收单一参数的函数并且返回接收余下参数且返回结果的新函数的技术。 通过柯里化我们可以将一个接受多个参数的函数转换为一个接受一个参数的函数序列。这意味着我们可以先传递一部分参数然后传递剩余的参数或者分别传递参数以此灵活地处理函数的调用。
例如下面是一个接受两个参数的普通函数
function add(a, b) {return a b
}通过柯里化我们可以将上述函数转换为一系列多个函数的调用
function add(x) {return function(y) {return x y;}
}进行调用
add(1, 2); // 3
add(1)(2) // 3通过柯里化我们可以轻松地创建具有更高可复用性和灵活性的函数。它在函数式编程中经常被使用并且可以用于创建高阶函数和函数组合。
3、实现原理
函数柯里化的实现原理是利用闭包和递归。 具体步骤如下
创建一个高阶函数用于接受原函数的参数并返回一个新函数。
在新函数内部使用闭包来保存已经传入的参数。
在新函数内部使用递归或者循环判断是否所有参数都已经传入。若是则执行原函数并返回结果若否则继续返回新函数接受下一个参数。
通过这样的方式就可以实现柯里化函数。 以下是一个简单的示例代码展示柯里化的实现
function currying(fn) {return function curried(...args) {if (args.length fn.length) {return fn.apply(this, args);} else {return function (...nextArgs) {return curried.apply(this, args.concat(nextArgs));};}};
}// 原函数
function add(x, y, z) {return x y z;
}// 柯里化后的函数
const curriedAdd currying(add);// 柯里化函数的调用
curriedAdd(1)(2)(3); // 返回 6// 也可以一次传入多个参数
curriedAdd(1, 2)(3); // 返回 6在上述代码中currying 函数是一个高阶函数用于接受原函数并返回柯里化后的函数。curried 函数是柯里化后的函数在每次调用时判断传入的参数数量是否满足执行原函数的条件。 通过递归调用每次返回一个新的函数直到传入的参数数量满足原函数的要求然后执行原函数并返回结果。这样就实现了函数的柯里化。
4、应用场景
4.1 参数复用
柯里化实际是把简答的问题复杂化了但是复杂化的同时我们在使用函数时拥有了更加多的自由度。 而这里对于函数参数的自由处理正是柯里化的核心所在。 柯里化本质上是降低通用性提高适用性。 我们工作中会遇到各种需要通过正则检验的需求比如校验电话号码、校验邮箱、校验身份证号、校验密码等 这时我们会封装一个通用函数 checkByRegExp ,接收两个参数校验的正则对象和待校验的字符串。
// 函数封装后
function checkByRegExp(regExp,string) {return regExp.test(string);
}checkByRegExp(/^1\d{10}$/, 18642838455); // 校验电话号码
checkByRegExp(/^(\w)(\.\w)*(\w)((\.\w))$/, test163.com); // 校验邮箱上面这段代码乍一看没什么问题可以满足我们所有通过正则检验的需求。 但是我们考虑这样一个问题如果我们需要校验多个电话号码或者校验多个邮箱呢 我们可能会这样做
checkByRegExp(/^1\d{10}$/, 18642838455); // 校验电话号码
checkByRegExp(/^1\d{10}$/, 13109840560); // 校验电话号码
checkByRegExp(/^1\d{10}$/, 13204061212); // 校验电话号码checkByRegExp(/^(\w)(\.\w)*(\w)((\.\w))$/, test163.com); // 校验邮箱
checkByRegExp(/^(\w)(\.\w)*(\w)((\.\w))$/, testqq.com); // 校验邮箱
checkByRegExp(/^(\w)(\.\w)*(\w)((\.\w))$/, testgmail.com); // 校验邮箱我们每次进行校验的时候都需要输入一串正则再校验同一类型的数据时相同的正则我们需要写多次 这就导致我们在使用的时候效率低下并且由于 checkByRegExp 函数本身是一个工具函数并没有任何意义 一段时间后我们重新来看这些代码时如果没有注释我们必须通过检查正则的内容 我们才能知道我们校验的是电话号码还是邮箱还是别的什么。 此时我们可以借助柯里化对 checkByRegExp 函数进行封装以简化代码书写提高代码可读性。
//进行柯里化
let _check currying(checkByRegExp);
//生成工具函数验证电话号码
let checkCellPhone _check(/^1\d{10}$/);
//生成工具函数验证邮箱
let checkEmail _check(/^(\w)(\.\w)*(\w)((\.\w))$/);checkCellPhone(18642838455); // 校验电话号码
checkCellPhone(13109840560); // 校验电话号码
checkCellPhone(13204061212); // 校验电话号码checkEmail(test163.com); // 校验邮箱
checkEmail(testqq.com); // 校验邮箱
checkEmail(testgmail.com); // 校验邮箱再来看看通过柯里化封装后我们的代码是不是变得又简洁又直观了呢。 经过柯里化后我们生成了两个函数 checkCellPhone 和 checkEmail checkCellPhone 函数只能验证传入的字符串是否是电话号码 checkEmail 函数只能验证传入的字符串是否是邮箱 它们与 原函数 checkByRegExp 相比从功能上通用性降低了但适用性提升了。 柯里化的这种用途可以被理解为参数复用
4.2 遍历数组
let list [{name:lucy,age: 20},{name:jack,age: 23}
]我们需要获取数据中的所有 name 属性的值常规思路下我们会这样实现:
let names list.map(function(item) {return item.name;
})那么我们如何用柯里化的思维来实现呢
let prop curry(function(key,obj) {return obj[key];
})
let names list.map(prop(name))看到这里可能会有疑问这么简单的例子仅仅只是为了获取 name 的属性值为何还要实现一个 prop 函数呢这样太麻烦了吧。 我们可以换个思路prop 函数实现一次后以后是可以多次使用的所以我们在考虑代码复杂程度的时候是可以将 prop 函数的实现去掉的。 我们实际的代码可以理解为只有一行 let names list.map(prop(name)) 这么看来通过柯里化的方式我们的代码变得更精简了并且可读性更高了。