Table of contents
Open Table of contents
关于 memoize
// 这里使用了数组作为缓存,特化于当前用例 ;
// 对于更加一般的情况,可以使用对象作为缓存 ,
// 以序列化后的函数参数作为 key , 计算结果作为 value ,
// 同时由于序列化带来的性能损失, 可以对参数是否为 primitive
// 作判断, 来避免不必要的序列化
//
// 参考: https://blog.risingstack.com/the-worlds-fastest-javascript-memoization-library/
const cache: number[] = []
const fib = (n: number) => {
if (!cache[n]) {
if (n <= 1) {
cache[n] = n
} else {
cache[n] = fib(n - 1) + fib(n - 2)
}
}
return cache[n]
}
const unMemoized = (n: number): number => {
if (n == 0 || n == 1) {
return n
} else {
return unMemoized(n - 1) + unMemoized(n - 2)
}
}
注入的例子:
type LogOption = {
preInvoke: Console['log']
startInvoke: Console['log']
postInvoke: Console['log']
}
const predefinedLogOption = {
preInvoke: console.log,
startInvoke: console.log,
postInvoke: console.log,
}
// 用于打印的函数属于 impure functions,
// 这里通过外部注入, 便于组合于测试
const logWrapper = <T extends (...args: any[]) => any>(
fn: T,
logOption: LogOption = predefinedLogOption // injecting impure functions
) => {
return function (...args: Parameters<T>): ReturnType<T> {
logOption.preInvoke(`${fn.name} called`)
logOption.startInvoke(`arguments are: `, ...args)
const result = fn(...args)
logOption.postInvoke(`result is `, result)
return result
}
}
const measureWrapper = <T extends (...args: any[]) => any>(fn: T) => {
return function (...args: Parameters<T>): ReturnType<T> {
const t0 = performance.now()
const result = fn(...args)
const t1 = performance.now()
console.log(`took ${t1 - t0} millseconds`)
return result
}
}
logWrapper(measureWrapper(fib))(1000)
impure 与 pure
// 此外,关于如何将 impure function 转换为 pure function ,
// 通过 delay evaluation 的方式来实现
// 比如, 网络请求函数, 可以这么写:
const fetcher = (url: string) => () => fetch(url)
// 由于每次都返回同样的函数, 所以是 pure 的
// 大费周章都要转换为 pure function 的目的是什么呢,
// 有以下几点:
// 1. 便于组合
// 2. 便于测试
// 3. 便于缓存
// 4. 便于调试
// 5. 便于维护
// 利用 bind 预置参数, 可以实现柯里化
const curry = (fn: (...args: any[]) => any) => {
return fn.length === 0 ? fn() : (p: any) => curry(fn.bind(null, p))
}
const sum = (x: number, y: number, z: number) => x + y + z
const curriedSum = curry(sum)
console.log(curriedSum(3)(40)(5))
// 柯里化的简单应用
const ImageExts = ['.jpg', '.png', '.jpeg', '.webp']
const isParticularExt = (extList: string[]) => (name: string) => {
return extList.some(ext => name.endsWith(ext))
}
const isImageExt = isParticularExt(ImageExts)
console.log(isImageExt('dog.jpg'), isImageExt('haha.mp4'))
oop 中设计模式的等效版本
1. decorator 与 wrapper
化为高阶函数, 以 react 中高阶组件举例:
import * as React from 'react'
const FullNameDisplay = (props: { firstName: string; lastName: string }) => {
return (
<div>
{props.firstName} {props.lastName}
</div>
)
}
// 想要附加一个边框的 feature, 可以这么写
const MakeVisible = (Component: ReactNode) => {
return <div style={{ border: '1px solid red' }}>{Component}</div>
}
Containers
1. 为何需要 containers
boolean, number, string 等基本数据类型不够灵活, 以及对其的操作没有统一的接口, 具体来说, 在 promise 中 , 我们可以用 then 不停地链式调用, 也就是在每次 then 后返回的都是 promise ( 同样的 containers ) 。
2. 基本的 container --- functor
class Functor<T> {
private _value: T
constructor(init: T) {
this._value = init
}
// of 方法用于创建 functor
static of<U>(init: U) {
return new Functor(init)
}
// map 方法用于映射 functor 的值
map<U>(fn: (param: T) => U) {
return Functor.of(fn(this._value))
}
// 拆开盒子, 拿出盒子里的值
value() {
return this._value
}
}
// 调用一下
console.log(
Functor.of(33)
.map(c => c + 43)
.map(c => `${c} years`)
.value()
)
// 结果为 "76 years"