一、函数柯里化
什么是函数柯里化
柯理化,也常以为“局部套用”,是把多参数函数转换为一系列单参数函数并进行调用的技术。 --- 《JavaScript语言精粹》
函数柯里化的好处
- 参数复用
- 提前返回
- 延迟计算/运算
上代码!!!no talking!
柯里化代码实现
例如一个简单的类型判断isType(typing, val),我们通常的调用方式为isType('String','aa'),这种调用方式很容易犯错,很容易将typing输入错误,导致错误的结果。
我们则希望通过类似isString(val)直接判断类型,利用函数柯里化的方式进行封装形成更通用的方式,详见优点一
function isType(typing, val) {
return Object.prototype.toString.call(val) == `[object ${typing}]`
}
柯里化优点一:参数复用和提高适用性
这里通过传入的参数长度决定调用方式,如果参数长度过短,如只传入typing未传入 val,先将参数缓存起来,直到参数传入完整,再调用整个函数
function curring(fn) {
const inner = (args = []) => {
return args.length >= fn.length ? fn(...args) : (...userArgs) => inner([...args, ...userArgs])
}
return inner()
}
function isType(typing, val) {
return Object.prototype.toString.call(val) == `[object ${typing}]`
}
let util = {};
['String', 'Number', 'Boolean', 'Null', 'Undefined'].forEach(type => {
util['is' + type] = curring(isType)(type)
})
util.isString('a')
util.isNumber('a')
util.isBoolean('a')
tips
这里12-13行代码有个小坑,如果12行末尾不增加;,会出现forEach报错问题,这里涉及到匿名数组的问题,问题详见下面链接。
节点.js - 类型错误:无法读取未定义的属性(读取 'forEach) - 堆栈溢出 (stackoverflow.com)
柯里化优点二:延迟执行
二、发布订阅模式
这是我们的需求:能够在读取a.txt和b.txt后,通知我们读取完成。
高阶函数写法
const fs = require('fs')
let arr = []
function after(times, callback) {
let arr = []
return (data, index) => {
arr[index] = data //保证顺序,需要索引
if (--times === 0) { //多个请求并发,需要靠计数器来实现
callback(arr)
}
}
}
let out = after(2, (arr) => {
console.log(arr)
})
fs.readFile('./a.text', 'UTF8', function (err, data) {
out(data, 0)
})
fs.readFile('./b.text', 'UTF8', function (err, data) {
out(data, 1)
})
发布订阅模式写法
const fs = require('fs')
let events = {
_events: [],
on(fn) {
this._events.push(fn)
},
emit(data) {
this._events.forEach(fn => fn(data))
}
}
events.on(() => {
console.log('每读取一次 就触发一次')
})
let arr = []
events.on((data) => {
arr.push(data)
})
events.on((data) => {
if (arr.length === 2) {
console.log('读取完毕', arr)
}
})
fs.readFile('./a.text', 'UTF8', function (err, data) {
events.emit(data)
})
fs.readFile('./b.text', 'UTF8', function (err, data) {
events.emit(data)
})
三、观察者模式
需求:存在两种类观察者(爸爸和妈妈),被观察者(宝宝),宝宝状态发生变化的时候,爸爸和妈妈需要能观察到宝宝状态发生了改变
class Subject { //被观察者的类 被观察者 需要将观察者收集起来
constructor(name) {
this.name = name
this.state = '非常开心'
this.observers = []
}
attach(o) {
this.observers.push(o)
}
setState(newState) {
this.state = newState
this.observers.forEach(o => o.update(this.name, newState))
}
}
class Observer { //观察者
constructor(name) {
this.name = name
}
update(s, state) {
console.log(this.name + ":" + s + "当前" + state)
}
}
let s = new Subject('小宝宝')
let o1 = new Observer('爸爸')
let o2 = new Observer('妈妈')
s.attach(o1)
s.attach(o2)
s.setState('不高兴了')