函数式编程
函数是指数学意义上的映射关系,而函数式编程是函数映射关系的抽象
纯函数
- 特点
- 相同输入相同输出。即函数返回值仅取决于函数参数值,也就是说,函数调用的结果不依赖于调用的时间和位置
- 没有副作用。在计算机科学中,函数的副作用是指当调用函数时,对外部产生附加的影响
纯函数是独立封闭的,既不依赖外部状态,也不修改外部状态
高阶函数
一个以函数作为函数参数或函数返回值的函数
柯里化函数(偏函数)
- 概念:采用递归降解方式实现多参函数,即只传递给函数部分参数调用,让它返回一个函数处理剩下参数
- 好处:提高多参函数的复用性
- 劣势:函数性能差,执行效率低。箭头函数、bind函数均优于柯里化函数
- 示例
js
// 实现一:需要对每个函数进行封装,支持调用时不带参则表明是返回结果
function sum(...rest) {
return function(...rest2) {
if(rest2.length === 0) {
return rest.reduce((s,i) => s + i, 0)
}
return sum(...rest, ...rest2)
}
}
console.log(sum(2,3,4,5,6,7)())
console.log(sum(2)(3,4)(5)(6,7)())
// 实现二:支持两次调用的柯里化函数
function add(num1,num2,num3) {
return num1 + num2 + num3;
}
function curry2(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
}
}
var r1 = curry2(add, 50, 1, 2);
var r2 = curry2(add, 50)(1, 2);
var r3 = curry2(add, 50, 1)(2);
// 实现三:无需对每个函数进行封装,不支持无限调用,超出函数参数长度,再调用报错
function curry(fn) {
const len = fn.length
return function wrapper(...rest) {
if(rest.length < len) {
return function(...rest2) {
return wrapper(...rest, ...rest2)
}
}
return fn(...rest)
}
}
curry(function(a,b,c) { return a + b + c })(1)(2)(3)
// [✅]实现四:支持无限次调用的柯里化函数。超出函数参数长度,再调用会报错
function curry(fn, ...args) {
return args.length >= fn.length? fn(...args) : function (...args2) {
return curry(fn, ...args, ...args2)
}
}
var r4 = curry(add)(50)(1)(2);
// 实现五:无需对每个函数进行封装,支持调用时不带参则表明是返回结果
function curry(fn) {
let _arg = []
return function wrapper(...rest) {
if(rest.length !== 0) {
_arg.push(...rest)
return wrapper
}
return fn(..._arg)
}
}
curry(function(a,b,c) { return a + b + c })(1)(2)(3)(4)() // 6
curry(function(a,b,c) { return a + b + c })(2,3)(5)(3)(4)() // 10
// 应用
const addEvent = (function(){
if(window.addEventListener) {
return function(el, type, fn, isCapture) {
el.addEventListener(type, fn, isCapture)
}
} else if(window.attachEvent) {
return function(el, type, fn) {
// el.attachEvent(`on${type.replace(/(?<=\b)[a-z]{1}/, val => val.toUpperCase())}`, fn)
el.attachEvent(`on${type}`, fn)
}
}
})()
const rafThrottle = function(fn) {
let isLocked = false
const raf = window.requestAnimationFrame || function(cb) {
window.setTimeout(cb, 1000 / 60)
}
return function() {
const context = this
const _args = arguments
if(isLocked) return
isLocked = true
raf(function(){
isLocked = false
fn.apply(context, _args)
})
}
}一次性函数
- 只执行一次的函数
js
function once(fn) {
let called = false
return function() {
if(!called) {
called = true
return fn.apply(this, arguments)
}
}
}惰性函数
- 只在第一次调用时执行,之后的调用都返回第一次调用的结果
js
function lazy(fn) {
let called = false
let result
return function() {
if(!called) {
called = true
result = fn.apply(this, arguments)
}
return result
}
}记忆函数
js
function memo(fn) {
const cache = {}
return (arg) => cache[arg] || (cache[arg] = fn(arg))
}函数组合
- 将多个函数组合成一个函数,从右向左执行,形如
fn(fn2(fn3()))
函子Functor
效果类似级联函数,即链式调用,形如a().b().c();
javascript
// 容器:包含值和值的变形关系,这个变形关系就是函数
// 函子:一个特殊的容器,通过一个普通对象来实现。该对象具有map方法,该方法可以运行一个函数对值进行处理
// Pointed函子
class Container {
static of(value) {
return new Container(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return Container.of(fn(this._value))
}
returnValue() {
return this._value
}
// TODO:待实现
// maybe函子:空值处理
// Either函子:异常处理
// IO函子:副作用处理
// Task函子:异步执行
// Monad函子:IO函子多层嵌套
}
Container.of(1).map(x => x + 1).map(x => x * x).returnValue() // 4
// Monad函子
class Result {
constructor(ok, err) {
this.ok = ok
this.err = err
}
isOk() {
return [null, undefined].includes(this.err)
}
map(fn) {
return this.isOk()? Result.of(fn(this.ok), this.err) : Result.of(this.ok, fn(this.err))
}
join() {
return this.isOk()? this.ok : this.err
}
flatMap(fn) {
return this.map(fn).join()
}
}
Result.of = function(ok, err) {
return new Result(ok, err)
}