Skip to content

Commit 41f1759

Browse files
committed
docs: 新增函数式编程篇
1 parent 293dab1 commit 41f1759

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
---
2+
title: 函数式编程篇
3+
tags:
4+
- ECMAScript
5+
---
6+
7+
# 函数式编程
8+
函数是指数学意义上的映射关系,而函数式编程是函数映射关系的抽象
9+
10+
## 纯函数
11+
- 特点
12+
- 相同输入相同输出。即函数返回值仅取决于函数参数值,也就是说,函数调用的结果不依赖于调用的时间和位置
13+
- 没有副作用。在计算机科学中,函数的副作用是指当调用函数时,对外部产生附加的影响
14+
> 纯函数是独立封闭的,既不依赖外部状态,也不修改外部状态
15+
16+
## 高阶函数
17+
一个以函数作为函数参数或函数返回值的函数
18+
### 柯里化函数(偏函数)
19+
- 概念:采用递归降解方式实现多参函数,即只传递给函数部分参数调用,让它返回一个函数处理剩下参数
20+
- 好处:提高多参函数的复用性
21+
- 劣势:函数性能差,执行效率低。箭头函数、bind函数均优于柯里化函数
22+
- 示例
23+
```js
24+
// 实现一:需要对每个函数进行封装,支持调用时不带参则表明是返回结果
25+
function sum(...rest) {
26+
return function(...rest2) {
27+
if(rest2.length === 0) {
28+
return rest.reduce((s,i) => s + i, 0)
29+
}
30+
return sum(...rest, ...rest2)
31+
}
32+
}
33+
console.log(sum(2,3,4,5,6,7)())
34+
console.log(sum(2)(3,4)(5)(6,7)())
35+
36+
// 实现二:支持两次调用的柯里化函数
37+
function add(num1,num2,num3) {
38+
return num1 + num2 + num3;
39+
}
40+
function curry2(fn) {
41+
var args = Array.prototype.slice.call(arguments, 1);
42+
return function() {
43+
var innerArgs = Array.prototype.slice.call(arguments);
44+
var finalArgs = args.concat(innerArgs);
45+
return fn.apply(null, finalArgs);
46+
}
47+
}
48+
var r1 = curry2(add, 50, 1, 2);
49+
var r2 = curry2(add, 50)(1, 2);
50+
var r3 = curry2(add, 50, 1)(2);
51+
52+
// 实现三:无需对每个函数进行封装,不支持无限调用,超出函数参数长度,再调用报错
53+
function curry(fn) {
54+
const len = fn.length
55+
return function wrapper(...rest) {
56+
if(rest.length < len) {
57+
return function(...rest2) {
58+
return wrapper(...rest, ...rest2)
59+
}
60+
}
61+
return fn(...rest)
62+
}
63+
}
64+
curry(function(a,b,c) { return a + b + c })(1)(2)(3)
65+
66+
// [✅]实现四:支持无限次调用的柯里化函数。超出函数参数长度,再调用会报错
67+
function curry(fn, ...args) {
68+
return args.length >= fn.length? fn(...args) : function (...args2) {
69+
return curry(fn, ...args, ...args2)
70+
}
71+
}
72+
var r4 = curry(add)(50)(1)(2);
73+
74+
// 实现五:无需对每个函数进行封装,支持调用时不带参则表明是返回结果
75+
function curry(fn) {
76+
let _arg = []
77+
return function wrapper(...rest) {
78+
if(rest.length !== 0) {
79+
_arg.push(...rest)
80+
return wrapper
81+
}
82+
return fn(..._arg)
83+
}
84+
}
85+
curry(function(a,b,c) { return a + b + c })(1)(2)(3)(4)() // 6
86+
curry(function(a,b,c) { return a + b + c })(2,3)(5)(3)(4)() // 10
87+
88+
// 应用
89+
const addEvent = (function(){
90+
if(window.addEventListener) {
91+
return function(el, type, fn, isCapture) {
92+
el.addEventListener(type, fn, isCapture)
93+
}
94+
} else if(window.attachEvent) {
95+
return function(el, type, fn) {
96+
// el.attachEvent(`on${type.replace(/(?<=\b)[a-z]{1}/, val => val.toUpperCase())}`, fn)
97+
el.attachEvent(`on${type}`, fn)
98+
}
99+
}
100+
})()
101+
102+
const rafThrottle = function(fn) {
103+
let isLocked = false
104+
const raf = window.requestAnimationFrame || function(cb) {
105+
window.setTimeout(cb, 1000 / 60)
106+
}
107+
return function() {
108+
const context = this
109+
const _args = arguments
110+
if(isLocked) return
111+
isLocked = true
112+
raf(function(){
113+
isLocked = false
114+
fn.apply(context, _args)
115+
})
116+
}
117+
}
118+
```
119+
120+
### 一次性函数
121+
- 只执行一次的函数
122+
```js
123+
function once(fn) {
124+
let called = false
125+
return function() {
126+
if(!called) {
127+
called = true
128+
return fn.apply(this, arguments)
129+
}
130+
}
131+
}
132+
```
133+
134+
### 惰性函数
135+
- 只在第一次调用时执行,之后的调用都返回第一次调用的结果
136+
```js
137+
function lazy(fn) {
138+
let called = false
139+
let result
140+
return function() {
141+
if(!called) {
142+
called = true
143+
result = fn.apply(this, arguments)
144+
}
145+
return result
146+
}
147+
}
148+
```
149+
150+
### 记忆函数
151+
```js
152+
function memo(fn) {
153+
const cache = {}
154+
return (arg) => cache[arg] || (cache[arg] = fn(arg))
155+
}
156+
```
157+
158+
### 函数组合
159+
- 将多个函数组合成一个函数,从右向左执行,形如`fn(fn2(fn3()))`
160+
161+
## 函子Functor
162+
效果类似级联函数,即链式调用,形如`a().b().c();`
163+
```javascript
164+
// 容器:包含值和值的变形关系,这个变形关系就是函数
165+
// 函子:一个特殊的容器,通过一个普通对象来实现。该对象具有map方法,该方法可以运行一个函数对值进行处理
166+
167+
// Pointed函子
168+
class Container {
169+
static of(value) {
170+
return new Container(value)
171+
}
172+
173+
constructor(value) {
174+
this._value = value
175+
}
176+
177+
map(fn) {
178+
return Container.of(fn(this._value))
179+
}
180+
181+
returnValue() {
182+
return this._value
183+
}
184+
185+
// TODO:待实现
186+
// maybe函子:空值处理
187+
// Either函子:异常处理
188+
// IO函子:副作用处理
189+
// Task函子:异步执行
190+
// Monad函子:IO函子多层嵌套
191+
}
192+
Container.of(1).map(x => x + 1).map(x => x * x).returnValue() // 4
193+
194+
// Monad函子
195+
class Result {
196+
constructor(ok, err) {
197+
this.ok = ok
198+
this.err = err
199+
}
200+
201+
isOk() {
202+
return [null, undefined].includes(this.err)
203+
}
204+
205+
map(fn) {
206+
return this.isOk()? Result.of(fn(this.ok), this.err) : Result.of(this.ok, fn(this.err))
207+
}
208+
209+
join() {
210+
return this.isOk()? this.ok : this.err
211+
}
212+
213+
flatMap(fn) {
214+
return this.map(fn).join()
215+
}
216+
}
217+
Result.of = function(ok, err) {
218+
return new Result(ok, err)
219+
}
220+
```
221+
222+
## 一类函数
223+
224+
## Lambda函数

0 commit comments

Comments
 (0)