偏函数、高阶函数、柯里化、匿名函数
好久没写博客,从一道题目开始吧
实现一个sum函数,支持sum(1,2)和sum(1)(2)两种调用方式
说实在的,没啥难的,很简单写出来
function sum(a, b) {
if (b) {
return a + b;
}
return (b) => a+b;
}
或者用ES6的展开操作符,或者是arguments
function sum(...args) {
if (args.length === 2) {
return args[0] + args[1];
}
if (args.length === 1) {
return (b) => b + args[0]
}
}
考察的内容其实就是函数作为返回值,简单涉及到了闭包,还有arguments的判断,ES6展开操作符的剩余语法。然后可以继续发散,ES6熟不熟啊,闭包理解到不到位啊,arguments是不是数组,怎么转成数组……
不写了,不写了,展开能问的太多了。今天主要的目的是写一写函数相关,解释一下那些常见的概念:偏函数、高阶函数、柯里化、匿名函数。
偏函数
偏函数(Partial application),wiki上解释
In computer science,partial application(orpartial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
大概意思,偏函数会固定一些参数,产生更少参数的函数。
改一下上面的题目:
实现一个sum函数,可以支持固定一个参数的的调用方式;如
var sum1 = sum(1);
sum1(2) // 3
function sum(base) {
return (num) => base + num;
}
var sum1 = sum(1);
或者使用bind
function sum(a, b) {
return a + b;
}
var sum1 = sum.bind(null, 1);
这里可以引申出bind的用法,bind和call的区别,call和apply的区别。就不多赘述了。之前写过一篇博客bind、apply与call。
高阶函数
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数: 接受一个或多个函数作为输入 输出一个函数
可以这么理解,如果可以接受函数作为入参和出参的,可以认为支持高阶函数。JS中,函数是一等公民,各种高阶函数随处可见,很容易写一个例子:
const print = console.log;
const foo = (a , b, printFn) => a + b;
foo(1, 2, print);
说个题外话,有这样一道题目:
[1,2,3].map(parseInt)
我感觉这个题目有点考api记忆了,不过如果熟练的话也就还好。map是一个高阶函数,接受一个函数作为参数,他会给入参函数传入两个值,item和index。parseInt接受两个参数,数值和进制。也就是执行
parseInt(1, 0);
parseInt(2, 1);
parseInt(3, 2);
答案[1, NaN, NaN]
我就不多说了。
柯里化
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
大概定义一下,可以认为curry之后的函数,可以接受一个参数,之后返回一个接受剩余参数的函数。
期望可以这么执行:
var add = curry((a, b) => a + b);
add(1, 2); // 可以正常运行
var add1 = add(1);
add1(2); // 也可以这么使用
可以发现,这不就是上面的题目吗?当然,稍有不同的地方在于,这里是通过curry把一个正常函数curry化。我们也来实现一个。
function curry (fn) {
return (...args) => {
if (args.length === fn.length) {
return fn.apply(null, args);
}
return fn.bind(null, args[0])
}
}
很明显,上面用到了偏函数的实现去做了curry,也可以不使用bind,在使用别的方法之前,加一些限定条件。
上面的curry简单处理了一个参数的情况,我们希望curry更智能一些,假设函数有10个参数,如果传了5个,又传了3个,又传了2个,只有最后一次才会返回结果。
// 为了使用递归,写了一个helper
function helper (fn, ...args) {
if (args.length === fn.length) {
return fn.apply(null, args);
}
return (...argsMore) => createFn(fn, ...args, ...argsMore);
}
function curry (fn) {
return (...args) => createFn(fn, ...args);
}
多说一个概念,thunk,阮一峰老师在他的博客里介绍过Thunk 函数的含义和用法,大家可以看一下,其实也就是curry的一处用法。
匿名函数
在计算机编程中,匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数或子程序,普遍存在于多种编程语言中。
wiki。
匿名函数相对命名函数而言,就是一些一次性使用的场景下,比如一些事件处理函数,可能只执行一次,可以使用匿名函数。
这个概念经常用,举个例子。
[1,2,3].map((item, index) => `item ${index}: ${item}`);
匿名函数的概念很简单,上面写的箭头函数容易考察一些this指向的问题。
比如为什么箭头函数不能使用new,怎么做到绑定this的。
这个问题,我打算留到下一篇写。嘿嘿嘿😝
完。感谢阅读。