{T}

编程方式

程序代码的可读性、可维护性在软件开发中尤为重要。所以在编写代码的时候通常会遵循一些编码规范或风格,比如 Google 提出了最著名的关于主流语言的风格指南,但这些都比较微观和具体,如果宏观且抽象地来看,编码风格可以上升为编程范式

编程范式(Programming Paradigm)也称 “编程泛型” 或“程序设计法”,是对代码编写方式的一种抽象,体现出开发者对程序执行的看法。

例如 在面向对象编程中,开发者认为程序是一系列相互作用的对象,而在函数式编程中一个程序会被看作是一个无状态的函数计算的序列。常见的编程范式有 2种:命令式编程和声明式编程

命令式编程

命令式编程是一种古老的编程范式,它的出现与冯 · 诺依曼架构有紧密关系。冯 · 诺依曼架构的基本工作原理是通过赋值语句来更改程序状态,然后根据这些状态来逐步执行任务。而命令式编程方式就是对这个工作过程的抽象,主要关注点是如何通过具体步骤得到计算结果。

命令式编程比较重要的子类有 2 个:面向过程、面向对象。

面向过程

面向过程是一种以过程为中心的编程思想,在编程过程中分析出解决问题所需要的步骤,然后再按照执行过程编写代码。

这种编程范式比较具象,很符合人的直觉思维。下面就是一个面向过程的例子,calc() 函数接收两个参数,第一个是待遍历的数组 arr,第二个是计算类型 type:

javascript
function calc(arr, type) {
  switch (type) {
    case 'add':
      return arr.reduce((acc, cur) => acc + cur, 0)
      break;
    case 'multiple':
      return arr.reduce((acc, cur) => acc * cur, 1)
      break;
  }
}

这种编程范式流程明确,也不需要像面向对象那样生成实例,占用额外的存储空间,但它有个问题,就是代码的可扩展性不够。比如现在要加一个操作类型,那么又要修改 calc() 函数,添加一个逻辑分支

面向对象

面向过程的编程范式要求按照流程步骤逐个地分析每个问题。很显然,并不是所有问题都适合这种过程化的思维方式,这也就导致了其他编程范式的出现,比如面向对象

面向对象的核心是对象,它不是把问题抽象成流程步骤,而是抽象成对象,对象是程序代码中的基本单位,对应代码中的类或类的实例,对象内部封装了数据和方法。这种编程范式主要包含 3 个特性:封装、继承、多态。

声明式编程

在计算机科学中,声明式编程是一种构建程序的样式,该表达式表示计算逻辑而无须谈论其控制流程。

它通常将程序视为某种逻辑理论,可以简化编写并行程序的过程。重点是需要完成的工作,而不是应该如何完成。它只是声明想要的结果,而并不关注这个结果如何产生,这是命令式(如何做)和声明式(做什么)编程范式之间的唯一区别。

声明式编程也有一些子类,常见的包括:逻辑式编程、数据驱动编程和函数式编程

逻辑式编程

逻辑式编程通过设置答案须符合的规则来解决问题,而非设置步骤来解决问题。

这种风格很符合数学家和哲学家分析问题的方式,当需要解答一个新的问题时,先提出一个新的假设,然后再证明它跟现在的理论无冲突。

逻辑提供了一个证明问题是真还是假的方法,创建证明的方法是人所皆知的,故逻辑是解答问题的可靠方法。逻辑式编程系统则自动化了这个程序,人工智能在逻辑式编程的发展中发挥了重要的影响

逻辑式编程在 Web 开发中并不常见

数据驱动编程

这种编程方法基于数据,程序语句由数据定义而非执行步骤

数据库程序是业务信息系统的核心,并提供文件创建、数据输入、更新、查询和报告功能。有几种主要针对数据库应用程序开发的编程语言,比如 SQL,它应用于结构化数据流,以进行过滤、转换、聚合(如计算统计信息)或调用其他程序

函数式编程

函数式编程范式来源于数学而非编程语言,它的关键原理是通过执行一系列的数学函数来得到结果。核心依赖是用于某些特定计算的功能,而非数据结构,也就是说,数据与函数是松散耦合的,甚至严格说,数据应该是不可见的,因为它隐藏在函数的实现内部

它主要有下面几个核心概念。

纯函数

若一个函数符合幂等性且无副作用那么就可以称为纯函数。幂等性是指在相同的输入值时,需产生相同的输出,与函数的输出和输入值以外的其他隐藏信息或状态无关

javascript
function rand() { 
  return Math.random() 
}
 
function zero() { 
  return 0 
}

副作用是指除了返回函数值之外,还对调用函数产生附加的影响。例如 修改全局变量(函数外的变量)、修改参数或改变外部存储。

javascript
function add(a, b){ 
  return a + b 
}
 
function setAddition(a, b) { 
  localstorage.setItem('sum', a + b) 
}

从上面的例子可以看出纯函数的输出可以不用和所有的输入值有关,甚至可以与所有的输入值都无关。

纯函数会带来很多优点:

  • 首先它是无状态的,这也就意味着函数内部不需要额外的存储空间来保存数据;
  • 其次具有高度的可测试性,在前端框架中体现比较明显,之前用 jQuery 这类库来开发项目时,代码测试非常困难,因为很多函数是不纯的,内部可能涉及 DOM 操作、AJAX 请求、浏览器存储等各种副作用操作

纯函数的使用也比较广泛,JavaScript 一些原生函数,例如 JSON.stringify() 就是纯函数,React 中也有与之对应的函数式组件。

高阶函数

高阶函数是指接收一个函数作为参数,然后返回另一个函数的函数。它体现的是一种高级的抽象思维,使用场景也比较多,例如 TypeScript 提供的装饰器功能来扩展函数,以及 React 中使用高阶组件来扩展组件行为。

柯里化

柯里化可以翻译成卡瑞化或加里化,是指把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数。这种处理函数的方式由克里斯托弗 · 斯特雷奇以逻辑学家哈斯凯尔 · 加里命名。

简单来说,就是在一个函数中预先填充几个参数,这个函数返回另一个函数,这个返回的新函数将其参数和预先填充的参数进行合并,再执行函数逻辑