题目一 手写函数柯里化
首先搞清楚什么是函数柯里化,是一种将n个参数的一个函数转换为n个函数一个参数的技术。
作用:参数复用,延迟执行
- 获取传入的函数的参数个数
- 返回函数中判断,
- 如果当前收集的函数参数没有到达最大值,则递归curry函数,将参数收集起来传入函数
- 否则则调用该函数传入的函数fn
// 使用于参数数量固定函数
var curry = function(fn, args) {
const len = fn.length // 代表传入函数的形参个数
args = args || []
return function() {
// 重新定义一个参数集合
let innerArgs = args.concat([...arguments]);
if (args.length >= len) {
return fn.apply(this, innerArgs);
} else { // 到达参数个数前收集函数
return curry.call(this, fn, innerArgs);
}
}
}
题目二 实现add(1)(2)(3)
写了上面的函数curry之后,这个就直接可以用了
function add(a, b, c) {
return a + b + c
}
fn = curry(add);
fn(2)(3)(4)
fn(2, 3)(4)
如果硬写,就是下面的,
function add(a) {
return function (b) {
return function (c) {
return a + b + c;
}
}
}
如果add函数改为不定参,则上面通过fn.length获取形参个数的方式就不生效,会返回0,因为fn.length是不包括rest参数的。所以需要标志来标识结束,假如用空参数代表调用结束,则可以一直搜集参数,直到想返回了,传空参数即可。
var curry2 = function(fn, args) {
args = args || []
return function() {
let innerArgs = args.concat([...arguments]);
if (!arguments.length) {
return fn.apply(this, innerArgs);
} else { // 到达参数个数前收集函数
return curry2.call(this, fn, innerArgs);
}
}
}
// 测试用例
function add(...args) {return args.reduce((a, b) => a + b)}
fn = curry2(add);
console.log(fn(2)(3)(4)())
console.log(fn(2,3)(4)(7)(9)())
console.log(fn(2,3)(4)(5)(6)())
题目三 偏函数实现
也是返回一个函数,偏函数是固定一些参数,产生更小元的函数,就是分两次传递参数,和bind的传参数的方式类似。
function partial(fn) {
const args = [].slice.call(arguments, 1);
return function () {
const innerArgs = args.concat([].slice.call(arguments));
return fn.apply(this, innerArgs);
}
}
// 测试用例
function sum(a, b, c) {
return a + b + c
}
var fn = partial(sum, 1);
console.log(fn(2, 3))
function sum2(...args) {
return args.reduce((a, b) => a + b)
}
fn2 = partial(sum2, 1, 2);
console.log(fn2(3, 4, 5))
偏函数和柯里化区别:
- 柯里化是将n个参数的函数转换为n个一个参数的函数
- 偏函数是将n个参数的函数转换成一个n-x参数的函数,x函数预置,参数进行复用。
题目四 函数组合实现
根据上面几个题,就想到了函数式编程的组合函数。
是将多个简单的函数组合成一个复杂的函数,每个函数的执行结果作为下一个函数的参数传递过去,最后一个函数的执行完就是结果。类似于linux中管道的概念,但是顺序跟它不一样。
- compose:函数从右向左执行
- pipe:函数从左向右执行
目的:
- 避免洋葱嵌套代码,提高可读性
- 组合单一功能生成多功能的新函数
- 可以把复杂函数拆成多个简单函数,然后组合后使用。
// 初级版
var compose = function(f, g) {
return function(x) {
return f(g(x));
}
}
// 遍历版本
var compose = function(...args) {
let start = args.length - 1;
return function () {
let i = start;
while (i >= 0) {
let result = args[i].apply(this, arguments)
return result;
}
}
}
// 传入一系列函数,从右向左执行,前一个函数的结果是下一个函数的参数,返回最后的结果
var compose = function(...fns) {
return function (...args) {
const first = fns.pop();
const firstResult = first.apply(this, args);
return fns.reduceRight((result, next) => next.call(this, result), firstResult)
};
}
function compose(...fns) {
return (value) => {
return fns.reduceRight((value, fn) =>{
return fn(value);
}, value);
}
}
// 测试用例:对输入内容进行格式处理,如,金额格式化:去除首尾空格、长度限制、千分位格式化。
var trim = (str) => String.prototype.trim.call(str)
var limit = (str) => String.prototype.substr.call(str,0,11)
var numberic = (str) => String(str).replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, "$1,")
var format = compose(numberic,limit,trim)
console.log(format(10000)) // 10,000
console.log(format(123000000)) // 123,000,000
console.log(format(' 123456789000000 ')) // 12,345,678,900
参考
函数式编程--函数组合(Function composition) JavaScript函数式编程之compose和pipe的理解和实现