这是我参与更文挑战的第12天,活动详情查看: 更文挑战
前言
最近在学习函数式编程,在学习的过程中顺便整理下当前常用的编程范式。
编程范式
计算机编程的基本风格或典范模式。
编程范式和编程语言关系
- 抽象和具体。编程范式是抽象的,只有在编程语言中才能体现出来。
- 多对多关系。
- 主导范式。一种语言通常会有一两种主导范式,成为这门语言的风格特征。
分类
我们常用的编程范式有命令式编程,声明式编程, 面向对象编程,函数式编程,泛型编程等。对于JS来讲,前四种算是比较常用。
1. 命令式编程
命令式编程就是传统的编程思路,上手就写,临时变量,各种循环等。它的主要思想就是关注计算机的执行步骤,一步步告诉计算机先做什么再做什么,就好像是一个指令序列。
2. 声明式编程
声明式编程以数据结构的形式来表达程序执行逻辑。它的主要思想是告诉计算机该做什么,但是不指定它具体怎么做。它不需要去存储一大堆临时变量来存储数据,也不包含循环控制代码,如for,while等
SQL语句就是典型的声明式编程。
3. 面向对象编程
面向对象编程属于命令式编程的一种抽象。面向对象编程的三个基本概念:封装,继承,多态;
面向对象编程的五个原则:
- 单一职责【一个类应该有且只有一个去改变它的理由,这意味着一个类应该只有一项工作】
- 开放封闭【对象或实体应该对扩展开发,对修改关闭】
- 里氏替换【对父类的调用同样适用于子类】
- 依赖倒置【高层次的模块不应该依赖低层次的模块】
- 接口隔离【客户端不应该被迫依赖使用不到的接口】
JS中面向对象是把逻辑与数据封装到函数与原型链中,通过函数原型链实现继承,而代码的运行逻辑与数据依然是封装在函数内,但是做了属性和方法的区分。
4. 函数式编程
函数式编程是与声明式编程有关联的,但是不局限于声明式编程。它的重点是函数第一位,把逻辑完全视为函数的计算。可以将函数作为入参传入,也可以将函数作为返回值返回。函数式编程完全依赖于表达式。
5. 泛型编程
泛型编程理念是要求其中算法以尽可能抽象的方式编写,而不依赖于将在其上执行这些算法的数据形式。
泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库),其语言机制就是模板。模板的精神就是参数化类型。在前端开发领域,很少接触,不做过多了解。
命令式与声明式区别
示例:假设要把一个文件类型的数组转化成一个对象数组存储
arr = ['txt', 'zip', 'excel']
newArr = [{type: 'txt'}, {type: 'zip'}, {type: 'excel'}]
命令式:
function imperativeFun() {
let arr = ['txt','zip', 'excel'];
let newArr = [];
for(let i = 0, len = arr.length; i < len; i++) {
let obj = {
type: arr[i]
}
newArr.push(obj);
};
return newArr;
}
声明式:
function declarativeFun() {
let arr = ['txt','zip', 'excel'];
return arr.map(function(item) {
return {
type: item
}
});
}
// map函数将遍历数组的整个过程抽离出来。让我们更关注我们想要的是什么,而不是怎么做。
// 可以看出,在大部分情况下,命令式编程都比较直观简单,维护的人容易理解。
// 但是如果花时间去学习声明式中可以抽离归整的部分,会给我们的编程带来很大的便捷,比如节省代码量,抽象工具函数等。
面向对象与函数式编程区别
- 函数式编程是把数据与逻辑封装到函数中,而不是类或者对象。
- 面向对象的任何一个原型方法都可以拿到this,可以轻易获取闭包的数据。但是函数式编程的每个函数都是纯函数,不会依赖外部状态。
- 面向对象编程(OOP)通过封装变化让代码更容易理解。函数式编程(FP)通过最小化变化让代码更容易理解。
性质 | 函数式 | 面向对象 |
---|---|---|
组合单元 | 函数 | 对象(类) |
编程风格 | 声明式 | 命令式 |
数据和行为 | 独立且松耦合的纯函数 | 与方法紧密耦合的类 |
状态管理 | 将对象视为不可边值 | 通过实例方法改变对象 |
程序流控制 | 函数与递归 | 循环与条件 |
线程安全 | 可并发编程 | 难以实现 |
封装性 | 一切都是不可变的,无须封装 | 需要保护数据的完整性 |
示例,给定一个数字组成的数组,计算他们的平方和
面向对象实现:
//ES5:
function Calculate(nums) {
this.nums = nums;
}
Calculate.prototype.square = function() {
var squares = [];
for(var i=0; i<this.nums.length; i++) {
squares.push(this.nums[i]*this.nums[i]);
}
return square;
}
Calculate.prototype.sum = function(arr) {
var res = 0;
for(var i=0; i<arr.length; i++) {
res += arr[i];
}
return res;
}
var test = new Calculate([1, 2, 3]);
test.sum(test.square()); //14
//ES6:
class Calculate {
constructor(nums) {
this.nums = nums;
}
square() {
let squares = [];
for(let i=0; i<this.nums.length; i++) {
squares.push(this.nums[i]*this.nums[i]);
}
return squares;
}
sum(arr) {
let res = 0;
for(let i=0; i<arr.length; i++) {
res += arr[i];
}
return res;
}
}
let test = new Calculate([1, 2, 3]);
let m = test.sum(test.square());
函数式实现:
const calculate = (nums) => nums.map(x => x*x).reduce((res, data) => res+data, 0);
calculate([1,2,3])
总结
Javascript本身就是多范式语言,所以在合适的地方使用合适的编程方式。它们彼此互不排斥,可以共存。