Hibop.github.io
Hibop.github.io copied to clipboard
关于函数式编程简介
TOC
Functional programming is a programming paradigm
1.treats computation as the evaluation of mathematical functions
2.avoids changing-state and mutable data
by wikipedia
三个要点: 编程范式,类数学函数, 避免数据和状态改变
编程范式从概念上来讲指的是编程的基本风格和典范模式。 换句话说其实就是程序员对于如何使用编程来解决问题的世界观和方法论。
- 声明式(Declarative) What ==> 表达式 函数式
- 命令式(Imperative) How ==> 语句(for,if, throw etc.) 面向对象
JavaScript 是一门基于原型(prototype-based)的多范式语言。
// 得到一个数组每个数据平方后的和
// 命令式
function mysteryFn (nums) {
let squares = []
let sum = 0 // 1. 创建中间变量
for (let i = 0; i < nums.length; i++) {
squares.push(nums[i] * nums[i]) // 2. 循环计算平方
}
for (let i = 0; i < squares.length; i++) {
sum += squares[i] // 3. 循环累加
}
return sum
}
// 以上代码都是 how 而不是 what...
// 函数式
const mysteryFn = (nums) => nums
.map(x => x * x) // a. 平方
.reduce((acc, cur) => acc + cur, 0) // b. 累加
面向对象语言的问题是,它们永远都要随身携带那些隐式的环境。
你只需要一个香蕉,但却得到一个拿着香蕉的大猩猩...以及整个丛林。
by Erlang 作者:Joe Armstrong
避免改变状态和可变数据 (immutable)
Shared mutable state is the root of all evil
共享可变状态是万恶之源
by Pete Hunt

const obj = { val: 1 }
someFn(obj)
console.log(obj) // ???
var let const 区别与使用
- var声明变量可以重复声明,而let不可以重复声明
- var是不受限于块级的,而let是受限于块级
- var会与window相映射(会挂一个属性),而let不与window相映射
- var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
- const声明之后必须赋值,否则会报错
- const定义不可变的量【引用型除外】,改变了就会报错
- const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错,
函数是“一等公民”(first class)
- 纯函数(Pure Functions)&& 引用透明
- 无副作用
纯函数:对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
const double = x => x * 2
const addFive = x => x + 5
const num = double(addFive(10))
num === double(10 + 5)
=== double(15)
=== 15 * 2
=== 30
// 非纯函数
var a = 11;
function add(x) {
return x+a;
}
add(5);
副作用(Side Effects)
- 更改文件系统
- 往数据库中插入记录
- 发送一个 http 请求
- 改变数据
- 打印 log
- 获取用户输入
- DOM 查询
- 访问系统状态 这并不是说,要禁止使用一切副作用,而是说,要让它们在可控的范围内发生。
滥用匿名函数
// 太傻了
const getServerStuff = function (callback) {
return ajaxCall(function (json) {
return callback(json)
})
}
// 这才像样
const getServerStuff = ajaxCall
// 下面来推导一下...
const getServerStuff
=== callback => ajaxCall(json => callback(json))
=== callback => ajaxCall(callback)
=== ajaxCall
// from JS函数式编程指南
const BlogController = (function () {
const index = function (posts) {
return Views.index(posts)
}
const show = function (post) {
return Views.show(post)
}
const create = function (attrs) {
return Db.create(attrs)
}
const update = function (post, attrs) {
return Db.update(post, attrs)
}
const destroy = function (post) {
return Db.destroy(post)
}
return { index, show, create, update, destroy }
})()
// 以上代码 99% 都是多余的...
const BlogController = {
index: Views.index,
show: Views.show,
create: Db.create,
update: Db.update,
destroy: Db.destroy,
}
// ...或者直接全部删掉
// 因为它的作用仅仅就是把视图(Views)和数据库(Db)打包在一起而已。
// from JS函数式编程指南
为何钟爱一等公民?
多包一层的写法最大的问题就是,一旦内部函数需要新增或修改参数,那么包裹它的函数也要改...
// 原始函数
httpGet('/post/2', function (json) {
return renderPost(json)
})
// 假如需要多传递一个 err 参数
httpGet('/post/2', function (json, err) {
return renderPost(json, err)
})
// renderPost 将会在 httpGet 中调用,
// 想要多少参数,想怎么改都行
httpGet('/post/2', renderPost)
why函数式编程?
- 可缓存性(Cacheable)
- 可测试性(Testable)
- 合理性(Reasonable)
- 并行代码(Parallel Code)
- 可移植性/自文档化(Portable / Self-Documenting)
函数式编程高阶用法
- 科里化(curry)
- 组合 (compose)
- 惰性求值
- 尾递归优化
link: https://slides.com/yuanhobop/functional-programming-in-javascript-c6aacd#/