JavaScript 中的函数式模式与命令式模式
如果/否则
如果(没有其他情况)
对数组求和
对于/如果 (1)
对于/如果 (2)
尽早打破循环
如果/否则 如果/否则
设置属性
修改数组
课程
嵌套 for 循环
空守卫
结尾
这篇文章的目的并非论证某种范式优于另一种,而只是展示一些常见的模式及其功能对等物。
如果我遗漏了什么模式,而你想看看,请在下方评论区留言。注意,请将你的模式精简到最小公分母。我可没法转换一个 100 行的函数!;)
如果/否则
至关重要的
const hour = 14
let greeting
if (hour < 18) {
greeting = 'Good day';
} else {
greeting = 'Good evening';
}
功能
现在可以if
作为函数重新使用getGreeting
。
一个:
const isDay = hour => hour < 18
const getGreeting = hour => isDay(hour) ? 'Good Day' : 'Good Evening'
const greeting = getGreeting (hour)
乙:
import ifElse from 'mojiscript/logic/ifElse'
const isDay = hour => hour < 18
const getGreeting = ifElse (isDay) (() => 'Good Day') (() => 'Good evening')
const greeting = getGreeting (hour)
如果(没有其他情况)
至关重要的
let name = 'joel'
if (name != null) {
name = name.toUpperCase()
}
功能
isNotnull
并且toUpperCase
是可重复使用的函数。name
不会被覆盖,而是upperName
会被创建。
import when from 'mojiscript/logic/when'
const isNotNull = obj => obj != null
const toUpperCase = when (isNotNull) (string => string.toUpperCase ())
const name = 'joel'
const upperName = toUpperCase (name)
对数组求和
至关重要的
const values = [1, 2, 3]
let sum = 0
for (const x of values) {
sum = sum + x
}
功能
不要变异sum
。
一个:
const values = [1, 2, 3]
const add = (x, y) => x + y
const sum = values.reduce(add)
乙:
import reduce from 'mojiscript/list/reduce'
const add = x => y => x + y
const sum = reduce (add) (0)
const values = [1, 2, 3]
sum (values)
对于/如果 (1)
至关重要的
const values = [1, 2, 3, 4, 5]
let evens = []
for (const x of values) {
if (x % 2 === 0) {
evens.push(x)
}
}
功能
不要变异evens
。
import filter from 'mojiscript/list/filter'
const values = [1, 2, 3, 4, 5]
const isEven = num => num % 2 === 0
const evens = filter (isEven) (values)
对于/如果 (2)
至关重要的
const values = [1, 2, 3, 4, 5]
for (const x of values) {
if (x % 2 === 0) {
console.log(`${x} isEven`)
}
}
功能
用于when
条件执行。
import map from 'mojiscript/list/map'
import when from 'mojiscript/logic/when'
const isEven = num => num % 2 === 0
const logWhenEven = when (isEven) (x => console.log (`${x} isEven`))
const values = [1, 2, 3, 4, 5]
map (logWhenEven) (values)
尽早打破循环
至关重要的
const values = [1, 2, 3]
let sum = 0
for (const x of values) {
if (x > 3) break
sum = sum + x
}
功能
reduceWhile
类似于reduce
,但接受谓词以提前“中断”。
import reduceWhile from 'mojiscript/list/reduceWhile'
const add = x => y => x + y
const lte3 = num => num <= 3
const sum = reduceWhile (() => lte3) (add) (0) (values)
如果/否则 如果/否则
至关重要的
const fn = temp => {
if (temp === 0) return 'water freezes at 0°C'
else if (temp === 100) return 'water boils at 100°C'
else return `nothing special happens at ${temp}°C`
}
fn(0) //=> 'water freezes at 0°C'
fn(50) //=> 'nothing special happens at 50°C'
fn(100) //=> 'water boils at 100°C'
功能
import cond from 'mojiscript/logic/cond'
import $ from 'mojiscript/string/template'
const fn = cond([
[0, 'water freezes at 0°C'],
[100, 'water boils at 100°C'],
[() => true, $`nothing special happens at ${0}°C`]
])
fn(0) //=> 'water freezes at 0°C'
fn(50) //=> 'nothing special happens at 50°C'
fn(100) //=> 'water boils at 100°C'
设置属性
至关重要的
const obj = {
one: 1
}
obj.two = 2
功能
不要改变原始对象,浅克隆它,然后添加新的道具。
注意:当对象可变时,必须进行深度克隆。如果对象不可变,则可以进行浅克隆,这具有明显的性能优势。
const obj = {
one: 1
}
const newObj = {
...obj,
two: 2
}
修改数组
至关重要的
const values = [1, 2, 3]
values.push(4)
功能
不要变异values
。
一个:
const values = [1, 2, 3]
const newValues = [...values, 4]
乙:
对于大型数组,使用像列表这样的不可变库来获得高性能的不可变数组。
import L from 'list'
const values = L.from([1, 2, 3])
const newValues = L.append(4, values)
课程
至关重要的
容易出错。
class Cat {
constructor() {
this.sound = 'Meow'
}
talk() {
return this.sound
}
}
const cat = new Cat()
const talk = cat.talk
cat.talk() //=> 'Meow'
talk() //=> Error: Cannot read property 'sound' of undefined
功能
将功能与数据分离以实现最大程度的可重用性。
const cat = {
sound: 'Meow'
}
const dog = {
sound: 'Woof'
}
const talk = animal => animal.sound
talk (cat) //=> 'Meow'
talk (dog) //=> 'Woof'
嵌套 for 循环
至关重要的
let box = ''
for (let y = 0; y < 5; y++) {
for (let x = 0; x < 5; x++) {
box = box + '* '
}
box = box + '\n'
}
功能
不再嵌套。不可变。
import reduce from 'mojiscript/list/reduce'
import range from 'mojiscript/list/range'
const makeCols = cols =>
reduce (acc => () => acc + '* ') ('') (range (0) (cols))
const makeBox = ({ cols, rows }) =>
reduce (acc => () => `${acc}${makeCols (cols)}\n`) ('') (range (0) (rows))
const box = makeBox ({ cols: 5, rows: 5 })
//=> * * * * *
//=> * * * * *
//=> * * * * *
//=> * * * * *
//=> * * * * *
并且可重复使用!
const makeTriangle = length =>
reduce
(acc => i => `${acc}${' '.repeat(length - i)}${makeCols (i + 1)}\n`)
('')
(range (0) (length))
const triangle = makeTriangle (5)
//=> *
//=> * *
//=> * * *
//=> * * * *
//=> * * * * *
空守卫
至关重要的
const toUpper = string => {
if (string != null) {
return string.toUpperCase()
}
}
功能
一个:
此示例将参数包装在一个Maybe
类型中,然后在最后将其解包。在典型的函数式编程应用中,您会Maybe
在整个应用中使用 ,因此无需对 进行包装和解包string
。因此,这比您通常看到的要冗长一些。
import S from 'sanctuary'
const toUpper = S.pipe ([
S.toMaybe,
S.map (string => string.toUpperCase ()),
S.maybeToNullable
])
// If you use `Maybe` throughout your app, this would be your `toUpper` function.
const toUpper = S.map (string => string.toUpperCase ())
乙:
maybe
是一个函数装饰器,仅在传入参数时执行函数。现在我们的空值保护可以复用了。更多关于函数装饰器的信息,请访问:函数式 JavaScript:函数装饰器(第二部分)#JavaScript
const maybe = func => (...args) =>
args.length === 0 || args[0] == null
? args[0]
: func(...args)
const toUpper = maybe(string => string.toUpperCase ())
结尾
我的文章非常注重函数式 JavaScript,如果您需要更多 FP,请在这里关注我,或在 Twitter 上关注我@joelnet!
更多文章
问我一些关于函数式编程的愚蠢问题
让我们一起制作一个 DEV.to CLI...
让我们讨论一下 JavaScript 的自动生成文档工具