函数式编程入门

面向对象:封装、继承、多台

函数式编程:面向过程,对运算过程进行抽象。

有输入有输出,有相同输入就有相同输出。“函数式编程”中的“函数”指的是输入与输出的映射关系

函数是一等公民?

原因:函数可以作为参数、返回值、存储到变量中,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)

好处:

  1. 可缓存:因为相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来(如memoize)
  2. 可测试
  3. 可以并行:多线程环境下并行操作共享内存数据很有可能出现意外情况(如操作全局变量),纯函数不需要访问共享的内存数据,所以在并行环境下可以任意运行纯函数(web worker)

副作用

纯:相同输入永远有相同输出,且没有任何可观察的副作用。副作用就是让一个函数不纯。

// 不纯
let a=1
function add(b){return a+b} // 当a的值改变没有相同的输出

// 纯的,有硬编码,后续通过柯里化解决
function add(b){
    let a=1
    return a+b
}

副作用的来源

如果函数依赖于外部的状态就无法保证输出相同,就会有副作用,如:

  • 配置文件
  • 全局变量
  • 数据库
  • 获取用户的输入

所有的外部交互都会带来副作用。副作用不会完全禁止,尽可能控制他们在可控范围内发生。

js