相关文章推荐
善良的牛肉面  ·  coffe1891/frontend-har ...·  2 年前    · 
善良的牛肉面  ·  标签| 前端指南·  2 年前    · 
善良的牛肉面  ·  聊聊编程范式- 知乎·  2 年前    · 
善良的牛肉面  ·  前端编程范式- 掘金·  2 年前    · 
  • 编程范式是一种编程风格,可分为命令式、函数式(属于声明式)、面向对象等多种范式。
  • JS 是一种动态语言,它支持多种范式,具体选择哪种范式,根据业务需求和个人风格做选择。
  • 编程范式没有绝对的好坏之分,只有合适和不合适。
  • 简单来说下这几种范式的特点。
  • 命令式更符合自然逻辑,容易理解。
  • 函数式减少了临时变量,容易维护。
  • 面向对象更方便扩展,代码复用程度高。
  • 1、编程范式是什么

    编程范式 是一类典型的编程风格,是指从事软件工程的一类典型的风格(可以对照方法学)。

    如:命令式编程、函数式编程、面向对象编程等不同的编程范式。

    前端领域的函数式编程的体现

    最近函数式编程逐渐又火了起来,我们看看前端领域有什么函数式编程的影子吧

  • ES6的箭头函数、map 、reduce 、filter
  • React 16.8 的Hook
  • Vue3.0的Composition API
  • 2、命令式编程

    命令式编程就是关注计算执行步骤,如何执行,注重过程。

    大部分命令式编程语言都支持四种基本的语句

  • 运算语句;
  • 循环语句(for、while);
  • 条件分支语句(if else、switch);
  • 无条件分支语句(return、break、continue)。
  • 常见表格带表头分组的需求,需要单独给每个列表配置宽度,而表格数据是动态获取的,所以要根据接口返回的数据生成一定数量重复的固定宽度数组,用来配置表格宽度。

  • 使用命令式的写法
  • const WIDTHS = ['10px','20px','30px'], LENGTH = 3;
    let arr = [];
    for (let i = 0; i < LENGTH; i++) {
      arr = arr.concat(WIDTHS);
    // ["10px", "20px", "30px", "10px", "20px", "30px", "10px", "20px", "30px"]
    
  • 使用函数式的写法
  • const WIDTHS = ['10px','20px','30px'], LENGTH = 3;
    const arr = Array(LENGTH).fill('').reduce(acc => acc.concat(WIDTHS), []);
    // ["10px", "20px", "30px", "10px", "20px", "30px", "10px", "20px", "30px"]
    

    命令式编程的优缺点

  • 性能高,因为有引擎作优化
  • 容易理解,因为符合自然编程思路
  • 产生大量临时变量
  • 代码可读性低,需要通读代码才知道具体做了什么
  • 3、函数式编程(FP)

    函数式编程,主要强调如何通过函数的组合变化来解决问题,关注结果。

    函数式编程的特性

  • 函数是"第一等公民"
  • 函数可以像其他数据类型一样操作,如赋值给其他变量、作为函数的入参、作为函数的返回值。
  • 只在需要的时候执行,不产生无意义的中间变量。
  • 没有"副作用"
  • 副作用指函数计算结果的过程中,系统状态的变化,或者函数内部和外部进行交互,产生其他影响。
  • 常见的副作用:更改全局变量、发送http请求、dom查询。
  • 引用透明性
  • 即如果提供同样的输入,那么函数总是返回同样的结果。
  • 完全不依赖外部状态的变化(无状态),如全局变量,this 指针,IO 操作等。
  • PS:没有副作用 + 无状态 又可以称为纯函数。
  • 柯里化(Currying)
  • 理解为“加工站”,接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数。
  • 将一个多元函数转为一个单元函数,可以依次调用f(x,y,z) → f(x)(y)(z)
  • 举个例子:求两个数的平方和
  • // 原始版本
    const squares = function(x, y) {
      return x * x + y * y;
    // 柯里化版本
    const currySquares = function(x) {
        return function(y) {
        	return x * x + y * y;
    console.log(squares(1,2));
    console.log(currySquares(1)(2));
    
  • 函数组合(Compose)
  • 理解为“流水线”,将多个函数组合成一个新的函数,初始数据通过多个函数依次处理,最后整体输出。
  • 把复杂的逻辑拆分成一个个简单任务,最后组合起来完成任务,使得整个过程的数据流更明确、可控。
  • 为了保证每个加工站的输出刚好流入下个工作站的输入,必须是单元函数。如果是加工站是多元函数,就需要用到柯里化转为单元函数,再放到流水线上组合使用。
  • 两个函数组合示例: 注意compose (从右往左的组合顺序执行 )
  • const compose = (f, g) => x => f(g(x))
    const f = x => x + 1;
    const g = x => x * 2;
    const fg = compose(f, g);
    fg(1) // 3
    
  • 多个函数组合示例: 如果需要类似管道pipe(从左往右)的数据流,将reduce换成reduceRight即可。
  • const compose = (...fns) => {
      return fns.reduce((acc,cur) => {
        return (...args) => {
          return acc(cur(...args))
    // 最后返回的函数先执行
    const f = x => {
      console.log('f: ', x);
      return x + 1;
    const g = x => {
      console.log('g: ', x);
      return x + 1;
    const t = x => {
      console.log('t: ', x);
      return x + 1;
    compose(f, g, t)(1);
    // t:  1
    // g:  2
    // f:  3
    

    函数式编程的优缺点

  • 代码简洁,开发快速
  • 函数复用率很高,减少重复,开发速度快
  • 减少状态变量的声明和维护
  • 更少的出错概率
  • 强调纯函数,没有副作用
  • 比如 使用命令式就只需要 变量+命令式(一层循环),使用函数式,不使用外部变量(双重循环)。
  • 在 JS 这种非函数式语言中,函数式的方式比直接写语句指令慢(引擎会针对很多指令做特别优化),如纯循环就比原生map性能快几倍。
  • 内存容易占用过高
  • 为了实现对象状态的不可变,创建更多新对象。消耗更多内存空间,JS引擎进行垃圾回收有压力。
  • let boy = new Person("Joe"); boy.touch(); // boy.touch is not a function boy.work(); // Joe is working hard Person.touch(); // Touch the fish
  • 继承就是子类可以继承父类,使得子类对象(实例)具有父类公有的属性和方法,不需要写重复的代码。
  • 子类继承父类后,子类就会拥有父类的属性和方法,同时子类还可以声明自己的属性和方法。
  • class Person {
      constructor(name) {
          this.name = name;
      work() {
          console.log(`${this.name} is working hard`);
      static touch() {
          console.log("Touch the fish");   
    class FatMan extends Person {
        constructor(name){
            super(name);
        drink() {
          console.log("喂!三点几了,饮茶先!");
    let uncle = new FatMan("饮茶哥");
    uncle.work(); // 饮茶哥 is working hard
    uncle.drink(); // 喂!三点几了,饮茶先!
    uncle.touch();    // uncle.touch is not a function
    
  • 多态按字面的意思就是“多种状态”,允许将子类类型的指针赋值给父类类型的指针。即同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
  • 多态的表现方式有重写,重载和接口,原生 JS 能够实现的多态只有重写。
  • 重写:重写是子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想直接地继承父类的方法,自己想做调整和改动,就要重写父类的方法。我们也可以称之为方法覆盖。
  • class Person {
      constructor(name) {
          this.name = name;
      getName() {
        console.log(this.name);
      work() {
          console.log(`${this.name} is working hard`);
      static touch() {
          console.log("Touch the fish");   
    class FatMan extends Person {
        constructor(name){
            super(name);
        work() {
        	console.log("做碌*啊做!");
        drink() {
          console.log("喂!三点几了,饮茶先!");
    const boy = new Person('Joe');
    const uncle = new FatMan('饮茶哥');
    boy.getName(); // Joe
    boy.work(); // Joe is working hard
    uncle.getName(); // 饮茶哥
    uncle.work(); // 做碌*啊做!
    

    5、如何选择

  • 学习编程范式的意义是丰富你的编程思路,在面对特定场景,选择最合适的编程武器,灵活组合使用。
  • 命令式、函数式、面向对象本质上并没有优劣之分。我们可以在实际开发中,根据需求选择合适的编程范式。
  • 浅析JavaScript函数式编程(mp.weixin.qq.com/s/oZd4Rbi9-…
  • 编写高质量可维护的代码:编程范式(mp.weixin.qq.com/s/NmWPziVRn…
  • 简明 JavaScript 函数式编程——入门篇(juejin.cn/post/684490…
  • 面向对象之三个基本特征(javaScript)(segmentfault.com/a/119000001…
  • 函数式编程库(ramda.cn/
    20小时前
  • 私信