• 快速了解函数式编程


    说明

    【跟月影学可视化】加餐篇学习笔记。

    两种编程范式:命令式与声明式

    编程范式有两种,分别是命令式(Imperative)声明式(Declarative)

    • 命令式:强调做的步骤也就是怎么做
    • 声明式:强调做什么本身,以及做的结果。

    编程语言也可以分成命令式和声明式两种类型

    • 命令式:可以分成过程式和面向对象
    • 声明式:可以分成逻辑式和函数式

    在这里插入图片描述

    JavaScript 同时拥有命令式和声明式的特征

    命令式的实现代码:

    let kaimo = [1, 2, 3, 4, 5, 6];
    let tempArr = [];
    for(let i = 0; i < kaimo.length; i++){
      tempArr.push(kaimo[i] * 2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    声明式的实现代码:

    let kaimo = [1, 2, 3, 4, 5, 6];
    const double = x => x * 2;
    kaimo.map(double);
    
    • 1
    • 2
    • 3

    函数式与纯函数

    函数是对过程的封装,但函数的实现本身可能依赖外部环境,或者有副作用(Side-effect)。所谓函数的副作用,是指函数执行本身对外部环境的改变。我们把不依赖外部环境和没有副作用的函数叫做纯函数,依赖外部环境或有副作用的函数叫做非纯函数

    例子1:add 是一个纯函数,它的返回结果只依赖于输入的参数,与调用的次数、次序、时机等等均无关。

    function add(x, y) {
      return x + y;
    }
    
    • 1
    • 2
    • 3

    例子2:getId 是一个非纯函数,它的返回值除了依赖于参数 id,还和外部环境(文档的 DOM 结构)有关。

    function getId(id) {
      return document.getElementById(id);
    }
    
    • 1
    • 2
    • 3

    例子3:join 也是一个非纯函数,它的副作用是会改变输入参数对象本身的内容。

    funciton join(arr1, arr2) {
      arr1.push(...arr2);
      return arr1;
    }
    
    • 1
    • 2
    • 3
    • 4

    纯函数的优点

    • 易于测试:纯函数不需要依赖外部环境
    • 可并行计算(时序无关)
      • 在浏览器中,可以利用 Worker 来并行执行多个纯函数
      • 在 Node.js 中,可以用 Cluster 来实现同样的并行执行
      • 使用 WebGL 的时候,纯函数有时候还可以转换为 Shader 代码,利用 GPU 的特性来进行计算。
    • 有良好的 Bug 自限性:纯函数不会依赖和改变外部环境,所以它产生的 Bug 不会扩散到系统的其他部分

    函数式编程范式与纯函数

    尽可能多设计纯函数,少设计非纯函数,这样能够提升系统的可测试性、性能优化空间以及系统的可维护性。

    如何让系统的纯函数尽可能多,非纯函数尽可能少呢?

    答案是用函数式编程范式

    例子:实现一个模块,用它来操作 DOM 中列表元素,改变元素的文字颜色

    // 设置一个 DOM 元素的文字颜色
    function setColor(el, color){
      el.style.color = color;
    }
    // 批量设置
    function setColors(els, color){
      els.forEach(el => setColor(el, color));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上面两个函数都改变了外部环境(DOM)所以它们是两个非纯函数。

    下面实现一个 batch 高阶函数 (High Order Function)来优化。

    所谓高阶函数,是指输入参数是函数,或者返回值是函数的函数。

    function batch(fn){
      return function(target, ...args){
        if(target.length >= 0){
          return Array.from(target).map(item => fn.apply(this, [item, ...args]));
        }else{
          return fn.apply(this, [target, ...args]);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样模块就可以减少为一个非纯函数。

    function setColor(el, color){
      el.style.color = color;
    }
    
    let setColors = batch(setColor);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    高阶函数与函数装饰器

    如果输入参数和返回值都是函数,这样的高阶函数又叫做函数装饰器(Function Decorators)

    当一个高阶函数是用来修饰函数本身的,它就是函数装饰器。也就是说,它是在原始函数上增加了某些带有辅助功能的函数。

    例子:实现一个通用的函数装饰器:当代码库要进行大版本升级,在未来最新的版本中我们想要废弃掉某些 API,需要做一个平缓过渡,给它们增加一个提示信息,告诉调用它们的用户,这些 API 将会在下一次升级中被废弃。

    function deprecate(fn, oldApi, newApi) {
      const message = `The ${oldApi} is deprecated. Please use the ${newApi} instead.`;
    
      return function(...args) {
        console.warn(message);
        return fn.apply(this, args);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在模块导出 API 的时候,对需要废弃的方法统一应用这个装饰器,就可以无侵入地修改模块的 API:

    // 引入要废弃的 API
    import {foo, bar} from './foo';
    ...
    // 用高阶函数修饰
    const _foo = deprecate(foo, 'foo', 'newFoo');
    const _bar = deprecate(bar, 'bar', 'newBar');
    
    // 重新导出修饰过的API
    export {
      foo: _foo,
      bar: _bar,
      ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    04 `Linux`的VIM
    元宇宙照进现实 金蝶联手科大讯飞发布“数字员工”
    【STM32】STM32学习笔记-WDG看门狗(46)
    编辑器库QsciScintilla的indicator点击没有按键信息的bug解决
    Wi-SUN归来,信驰达携手TI发力广域自组网
    Excel怎么批量生成文件夹
    5 - 2 单选题
    接口自动化测试的概述及流程梳理~
    从0到1CTFer成长之路——1git(全网最详细)
    java毕业生设计中小学家校通系统计算机源码+系统+mysql+调试部署+lw
  • 原文地址:https://blog.csdn.net/kaimo313/article/details/126830679