美文网首页
学习笔记(一)—— 函数式编程

学习笔记(一)—— 函数式编程

作者: 彪悍de文艺青年 | 来源:发表于2020-08-25 16:16 被阅读0次

    最近参加拉勾教育大前端高薪训练营,开始对前端知识体系进行一个系统的扩展跟学习,通过每天定期的学习,对一些不常使用的知识点进行了了解和补充,同时对已经使用过的知识点温故而知新
    在此记录学习笔记,并根据学习进度定时跟新

    函数一等公民 (First-class Function)

    • 函数可以存储在变量中
    • 函数可以作为参数
    • 函数可以作为返回值

    高阶函数(Higher-order Function)

    • 可以把函数作为参数传递给另一个函数
    • 可以把函数作为另一个函数的返回结果

    常用的高阶函数及模拟实现

    • forEach
    // forEach
    const forEach = (array, fn) => {
        for (let t of array) {
            fn(t)
        }
    }
    
    • filter
    // filter
    const filter = (array, fn) => {
        let result = []
        for (let t of array) {
            fn(t) && result.push(t)
        }
        return result;
    }
    
    • map
    // map
    const map = (array, fn) => {
        let result = []
        for (let t of array) {
            result.push(fn(t))
        }
        return result
    }
    
    • some
    // some
    const some = (array, fn) => {
        for (let t of array) {
            if(fn(t)) {
                return true
            }
        }
        return false
    }
    
    • every
    // every
    const every = (array, fn) => {
        for (let t of array) {
            if(!fn(t)) {
                return false
            }
        }
        return true
    }
    
    • once
    // once
    const once = (fn) => {
        let done = false
        return (...args) => {
            if (!done) {
                done = true
                return fn.apply(this, args)
            }
        }
    }
    

    闭包 (Closure)

    • 函数及其周围的状态的引用捆绑在一起,组成闭包
      * 可以在另一个作用域中调用一个函数的内部函数,并访问到该函数的作用域中的成员

    • 闭包的本质:函数在执行时会放到一个执行栈上,当执行完成后会从执行栈上移除,但是堆上的作用域成员因为被外部引用而不能被释放,因此内部函数依然可以访问外部函数的成员

    • 箭头函数,闭包中this指向undefined,因为外层函数调用栈被释放?

    纯函数

    • 相同的输入永远得到相同的输出,且没有任何可观察的副作用
    • 数组的slice和splice分别是纯函数和不纯函数
      * slice返回数组指定部分,不会改变原数组
      * splice从原数组中移除的元素,并返回移除元素集合,会改变原数组
    • 函数式编程不会保留计算中间的结果,所以变量是不可变的(无状态的)

    Lodash

    具体参见Lodash官网

    纯函数的好处

    • 可缓存

      • 模拟实现lodash memoize
      function memoize (fn) {
          let cache = {}
          return function () {
              const key = JSON.stringify(arguments)
              cache[key] = cache[key] || fn(...arguments)
              return cache[key]        
          }
      }
      
    • 可测试
    • 并行处理
    • 纯函数不需要访问共享内存数据

    副作用

    • 使纯函数变得不纯
    • 副作用来源:外部数据依赖(接口、数据库、配置文件、全局变量等)
    • 副作用不可能完全禁止,尽可能控制在可控范围内

    柯里化(Currying)

    • 当一个函数包含多个参数的时候先传递部分参数去调用他(这部分参数以后永远不变)
    • 然后返回一个新的函数接收剩余参数,并返回结果
    • 柯里化模拟实现
    // curry
    function curry (fn) {
        return function (...args) {
            if (args.length < fn.length) {
                return function() {
                    return fn(...args, ...arguments)
                }
            }
            return fn(...args)
        }
    }
    
    • 柯里化总结
      • 柯里化可以使我们给一个函数传递较少的参数,生成一个记住了某些参数的新函数
      • 是一种对函数的缓存
      • 使函数的粒度更小
      • 可以将多元(多参数)函数转化为一元(单参数)函数

    函数组合(compose)

    • 函数组合可以把细粒度的函数重新组合成新的函数

    • 如果一个函数需要多个函数处理才能得到最终值,我们可以将中间过程的函数合并成一个函数

    • 函数组合默认从右向左执行

    • compose函数模拟实现

      // compose
      const compose = (...fns) => (...args) => fns.reduceRight((fn1, fn2) => ()=> fn2(fn1(...args)))()
      
    • lodash中的组合函数

      • flow() 从左向右组合执行
      • flowRight() 从右向左组合执行
    • lodash/fp模块

      • 提供了对函数式编程友好的方法
      • fp模块中提供的方法(例如:map、filter、split等)都是被柯里化的
      • 多个参数的方法函数优先,数据滞后(默认为数据优先、函数滞后,例如map(array, func))
    • lodash-map方法的小问题:parseInt参数问题

      • map(array, func(item, index, array))
      • parseInt(string, radix)
        • string 待parse字符串
        • radix 进制
      • map传递给parseInt 3个参数,将index当成了radix,由此引发问题
      • fp.map柯里化后的func只接收一个参数,因此没有此问题
    • 函数组合只能组合单参数函数

    • PointFree

      • 不需要指明处理的数据
      • 只需要合成运算过程
      • 需要定义一些辅助的基本运算函数

    函子(Functor)

    • 容器:包含值和值的变形关系(变形关系即函数)

    • 函子:是一个特殊的容器,通过一个普通对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行变形处理

    • 函子用来处理副作用、异步操作、异常处理等

    • 函子对象class实现

      class Functor {
          constructor(value) {
              this._value = value
          }
          static of(value) {
              return new this.prototype.constructor(value)
          }
          map(fn) {
              return this.of(fn(this._value))
          }
      }
      
    • 总结

      • 函数式编程的运算不直接操作值,而由函子完成
      • 函子就是一个实现了map契约的对象
      • 函子中封装了一个值,想要操作值,就给map传递一个处理值的函数(纯函数)
      • 最终返回一个新的函子
    • 函子介绍

      • MayBe函子
        • 用于处理空值(null、undefined)
      class MayBe extends Functor {
          isNothing() {
              return this._value === null || this._value === undefined
          }
          map(fn) {
              return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
          }
      }
      
      • Either函子
      • 用于处理异常
      • 类似于if...else
      • Left函子 + Right函子
      • IO函子
        • IO函子的value是一个函数,将函数作为值来处理
        • IO函子可以把不纯的动作存储到_value中,延迟执行(惰性执行)
      class IO extends Functor {
          static of(value) {
              return new IO(function() {
                  return value
              })
          }
          map(fn) {
              return new IO(fp.flowRight(fn, this._value))
          }
      }
      
      • Task函子
        • 用于处理异步任务
      const { task } = require('folktale/concurrency/task')
      const { split, find } = require('lodash/fp')
      const fs = require('fs')
      
      function readFile(filename) {
          return task(resolver => {
              fs.readFile(filename, 'utf-8', (err, data) => {
                  if(err) {
                      resolver.reject(err)
                  }
                  resolver.resolve(data)
              })
          })
      }
      
      readFile('package.json')
          .map(split('\n'))
          .map(find(x => x.includes('version')))
          .run()
          .listen({
              onRejected: e => console.log(e),
              onResolved: d => console.log(d),
          })
      
      • Pointed函子
        • 实现了of静态方法的函子
        • of静态方法避免了使用new 来创建对象
        • 更深层的含义是of方法用来把值放到上下文Context(把值放到容器中,使用map来处理)
      • Monad函子
        • 一个函子具有join和of两个方法,并遵守一定规律,就是Monad函子
        • 可以变扁的Pointed函子
    • folktale 一个标准的函数式编程库

      • 提供了compose、curry等函数
      • 提供了Task、Maybe、Either等函子
      • 用法自行百度

    相关文章

      网友评论

          本文标题:学习笔记(一)—— 函数式编程

          本文链接:https://www.haomeiwen.com/subject/pammuttx.html