函数式编程入门
面向对象:封装、继承、多台
函数式编程:面向过程,对运算过程进行抽象。
纯
有输入有输出,有相同输入就有相同输出。“函数式编程”中的“函数”指的是输入与输出的映射关系
函数是一等公民?
原因:函数可以作为参数、返回值、存储到变量中,new Function(
alert(1))
高阶函数
意义:只需关注目标、屏蔽细节。对通用问题的抽象,可以使代码更简洁。
模拟常用方法,如:map、every、some
闭包
调用返回的函数,外部对函数内部的成员有引用。
闭包的本质:函数再执行的时候会放到一个执行栈上,当函数执行完毕之后会从执行栈上删除,但是堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数。
闭包的好处:延长了外部函数中内部变量的作用范围
扩展:js堆和栈的区别
栈会自动分配内存空间,会自动释放。堆动态分配的内存,大小不定也不会自动释放。
基本类型会放在栈中,引用类型会放在堆上。复制的时候使复制的指针,使栈上的地址,而不是堆上的对象。
纯函数
相同输入有相同输出,没有副作用(不会改变输入的参数)。
纯vs不纯
如:splice(不纯、改变原数组,相同的输入没有相同输出) slice(纯,没有改变输入)
纯函数的好处
lodash memoize
// 模仿lodash memoize
function memoize(func){
let cache={};
return function(){
const key=JSON.stringify(arguments)
cache[key]= cache[key]||func.apply(null,arguments)
return cache[key]
}
}
// test
const add=(a,b)=>{
console.log('work')
return a+b
}
const minus=(a,b)=>{
console.log('minus work')
return a-b
}
const foo=memoize(add)
const bar=memoize(minus)
console.log(foo(1,2))
console.log(foo(1,2))
console.log(foo(3,4))
console.log(foo(3,4))
console.log(bar(3,1))
/*输出
work
3
3
work
7
7
minus work
2
*/
记忆函数(见code)
好处:
- 可缓存:因为相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来(如memoize)
- 可测试
- 可以并行:多线程环境下并行操作共享内存数据很有可能出现意外情况(如操作全局变量),纯函数不需要访问共享的内存数据,所以在并行环境下可以任意运行纯函数(web worker)
副作用
纯:相同输入永远有相同输出,且没有任何可观察的副作用。副作用就是让一个函数不纯。
// 不纯
let a=1
function add(b){return a+b} // 当a的值改变没有相同的输出
// 纯的,有硬编码,后续通过柯里化解决
function add(b){
let a=1
return a+b
}
副作用的来源
如果函数依赖于外部的状态就无法保证输出相同,就会有副作用,如:
- 配置文件
- 全局变量
- 数据库
- 获取用户的输入
所有的外部交互都会带来副作用。副作用不会完全禁止,尽可能控制他们在可控范围内发生。