携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情
- 第一种情况:一个函数返回一个函数,那么这个函数就被称之为高阶函数。
function fn() {
return function() {
}
}
- 第二种情况:一个函数的参数是函数,那么照顾函数就被称之为高阶函数。
function fn(cb) {
cb()
}
fn(() => {})
我们利用高阶函数能解决什么问题?
1、进行函数的扩展
如果做一些扩展的时候,可能就会用到高阶函数
- 底层的一个核心方法:在使用的时候,不希望改变原来的核心方法
function core(a, b, c) { // 底层方法,不希望被修改
console.log("core", a, b, c)
}
- 我们可以新增一层,对其进行扩展
// 使用
// newCore传入一个函数,希望在 core 之前执行
let newCore = core.before() => {
console.log("before")
}
newCore(1, 2, 3)
实现 core.before
core.before = function(cb) { // cb 就是用户传入的函数(回调函数)
return (...args) => {
cb() // 先执行回调函数
this(...args) // 再执行原始core方法:this会向上找到core(箭头函数没有this、arguments、prototype)
}
}
此时运行的结果:
before
core
如果所有函数都希望有 before 方法的话
在函数原型 Function 上添加 before 方法
Function.prototype.before = function(cb) {
return (...args) => {
cb()
this(...args)
}
}
2、函数柯里化
例如:判断一个数据类型
- typeof:只能判断基本的数据类型
- 缺点:typeof null === 'object'
- instanceof: xxx instanceof xxx
- constructor:
{}.constructor = Object、[].constructor = Array - Object.prototype.toString.call
function isTyping(typing, value) {
// [object Null]
return Object.prototype.toString.call(value).slice(7) === typing
}
isTyping("String", "abc")
这种方式判断类型,每次都要传入类型字符串(例如 String、Number),很麻烦。我们可以利用闭包的机制来缓存变量。
闭包:定义函数的作用域和执行函数的作用域不一致,就会产生闭包
// 通过高阶函数暂存用户的参数
function isTyping(typing) {
// typing = "String" 会一直保存在这里(闭包)
return function(value) { // 调用之后,这里不会销毁,因为外界还保留着 isString 这个函数
return Object.prototype.toString.call(value).slice(7) === typing
}
}
let isString = isTyping("Stirng")
isString("abc")
封装到一个公用的对象中:
let util = {};
["String", "Number", "Boolean"].forEach(method => {
util["is" + method] = isTyping(method);
})
util.isString("abc");
util.isNumber(123);
柯里化的概念
将函数的多个入参,将其变成了分批传入参数,而且参数每次只能传递一个(会让函数变得更具体一些)
例如:
sum(1, 2, 3, 4, 5, 6)
// 可以自己实现一个柯里化函数,变为
sum(1)(2)(3)(4)(5)(6)
// 或者另外一种情况:偏函数(分批传参),参数可能是多个(有人把偏函数也叫做柯里化)
sum(1, 2)(3, 4)(5)(6)
柯里化要求函数的参数个数是已知的。
实际应用
有多个接口,每次我们都需要记录接口的返回值:
- 可以用数组,把每个接口的返回值存在数组中
- 也可以用柯里化的思想,每次拿到返回值就传入柯里化函数,知道数据都传完了,再做某件事
题目
如何将 sum(1, 2, 3, 4, 5, 6) 转化为下面两种形式的调用
sum(1)(2)(3)(4)(5)(6)
sum(1, 2)(3, 4)(5)(6)
代码实现:
function add(...args) { // 求和函数
return args.reduce((pre, cur) => pre + cur);
}
// 柯里化
function currying(fn) {
let args = []
return function temp(...newArgs) {
if(newArgs.length) {
args = [
...args,
...newArgs
]
return temp
} else {
let value = fn.apply(this, args)
args = [] // 保证下次调用时情况
return value
}
}
}
// 调用方法
let sum = currying(add)
console.log(sum(1)(2)(3)(4)(5)(6)())
console.log(sum(1, 2)(3, 4)(5)(6)())
总结
高阶函数可以缓存变量、实现功能
- 高阶函数可以实现扩展功能
- 高阶函数可以实现参数的缓存功能