作者:Jakob Klamser

翻译:疯狂的技术宅

原文:https://dev.to/jakobkit/oop-v...

未经容许严禁转载

介绍

先简要介绍一下面向对象和函数式编程。

两者都是编程范式,在容许和禁止的技术上有所不同。

有仅反对一种范式的编程语言,例如 Haskell(纯函数式)。

还有反对多种范式的语言,例如 JavaScript,你能够用 JavaScript 编写面向对象的代码或函数式代码,甚至能够将两者混合。

创立我的项目

在深刻探索这两种编程范式之间的差别之前,先创立一个阶乘计算器我的项目。

首先创立所需的所有文件和文件夹,如下所示:

$ mkdir func-vs-oop$ cd ./func-vs-oop$ cat index.html$ cat functional.js$ cat oop.js 

接下来在 index.html 内创立一个简略的表单。

<!DOCTYPE html><html><head>  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">  <script src="functional.js" defer></script></head><body>  <div class="container mt-5">    <div class="container mt-3 mb-5 text-center">      <h2>Functional vs OOP</h2>    </div>    <form id="factorial-form">      <div class="form-group">        <label for="factorial">Factorial</label>        <input class="form-control" type="number" name="factorial" id="factorial" />      </div>      <button type="submit" class="btn btn-primary">Calculate</button>    </form>    <div class="container mt-3">      <div class="row mt-4 text-center">        <h3>Result:</h3>        <h3 class="ml-5" id="factorial-result"></h3>      </div>    </div>  </div></body></html>

为了使界面看上去不那么俊俏,咱们把 bootstrap 作为 CSS 框架。

如果在浏览器中显示这个 HTML,应该是这样的:

当初这个表单还没有任何操作。

咱们的指标是实现一种逻辑,在该逻辑中你能够输出一个最大为 100 的数字。单击“Calculate”按钮后,后果应显示在 result-div 中。

上面别离以面向对象和函数式的形式来实现。

函数式实现

首先为函数式编程办法创立一个文件。

$ cat functional.js

首先,须要一个在将此文件加载到浏览器时要调用的函数。

该函数先获取表单,而后把咱们须要的函数增加到表单的提交事件中。

function addSubmitHandler(tag, handler) {  const form = getElement(tag);  form.addEventListener('submit', handler);}addSubmitHandler("#factorial-form", factorialHandler);

首先申明一个名为 addSubmitHandler 的函数。

这个函数有两个参数,第一个是要在 HTML 中查找的标签,第二个是要绑定到 Elementcommit-event 的函数。

接下来,通过传入#factorial-form 和函数名 factorialHandler 来调用此函数。

标签后面的 # 表明咱们正在寻找 HTML 中的 id 属性。

如果当初尝试运行该代码,则会抛出谬误,因为在任何中央都还没有定义函数 getElementfactorialHandler

因而,首先在 addSubmitHandler 函数后面定义 getElement ,如下所示:

function getElement(tag) {  return document.querySelector(tag);}

这个函数非常简单,只返回通过传入的标记找到的 HTML元素。然而稍后咱们将重用此性能。

当初增加 factorialHandler 函数来创立外围逻辑。

function factorialHandler(event) {  event.preventDefault();  const inputNumber = getValueFromElement('#factorial');  try {    const result = calculateFactorial(inputNumber);    displayResult(result);  } catch (error) {    alert(error.message);  } }

把事件传回后立刻调用 preventDefault

这将阻止 Submit 事件的默认行为,你能够试试不调用 preventDefault 时单击按钮后会产生什么。

之后,通过调用 getValueFromElement 函数从输出字段中获取用户输出的值。在失去数字后,用函数 calculateFactorial 计算阶乘,而后通过将后果传递给函数 displayResult 将后果展现到页面。

如果该值的格局不正确或者数字大于 100,将会抛出谬误并弹出 alert。

下一步,创立另外两个辅助函数: getValueFromElementdisplayResult,并将它们增加到 getElement 函数前面。

function getValueFromElement(tag) {  return getElement(tag).value;}function displayResult(result) {  getElement('#factorial-result').innerHTML = result}

这两个函数都应用咱们的 getElement 函数。这种可重用性是为什么函数式编程如此无效的一个起因。

为了使它更加可重用,能够在 displayResult 上增加名为 tag 第二个参数。

这样就能够动静设置应该显示后果的元素。

然而在本例中,我用了硬编码的形式。

接下来,在 factoryHandler 后面创立 calculateFactorial 函数。

function calculateFactorial(number) {  if (validate(number, REQUIRED) && validate(number, MAX_LENGTH, 100) && validate(number, IS_TYPE, 'number')) {    return factorial(number);  } else {    throw new Error(      'Invalid input - either the number is to big or it is not a number'    );  }}

接着创立一个名为 validate 的函数来验证参数 number 是否为空且不大于 100,且类型为 number。如果查看通过,就调用 factorial 函数并返回其后果。如果没有通过,则抛出在 factorialHandler 函数中捕捉的谬误。

const MAX_LENGTH = 'MAX_LENGTH';const IS_TYPE = 'IS_TYPE';const REQUIRED = 'REQUIRED';function validate(value, flag, compareValue) {  switch (flag) {    case REQUIRED:      return value.trim().length > 0;    case MAX_LENGTH:      return value <= compareValue;    case IS_TYPE:      if (compareValue === 'number') {        return !isNaN(value);      } else if (compareValue === 'string') {        return isNaN(value);      }    default:      break;  }}

在这个函数中,用 switch 来确定要执行的验证范式类型。这只是一个简略的值验证。

而后在 calculateFactorial 申明的后面增加理论的 factor 函数。这是最初一个函数。

function factorial(number) {  let returnValue = 1;  for (let i = 2; i <= number; i++) {    returnValue = returnValue * i;  }  return returnValue;}

最终的 functional.js 文件下所示:

const MAX_LENGTH = 'MAX_LENGTH';const IS_TYPE = 'IS_TYPE';const REQUIRED = 'REQUIRED';function getElement(tag) {  return document.querySelector(tag);}function getValueFromElement(tag) {  return getElement(tag).value;}function displayResult(result) {  getElement('#factorial-result').innerHTML = result}function validate(value, flag, compareValue) {  switch (flag) {    case REQUIRED:      return value.trim().length > 0;    case MAX_LENGTH:      return value <= compareValue;    case IS_TYPE:      if (compareValue === 'number') {        return !isNaN(value);      } else if (compareValue === 'string') {        return isNaN(value);      }    default:      break;  }}function factorial(number) {  let returnValue = 1;  for (let i = 2; i <= number; i++) {    returnValue = returnValue * i;  }  return returnValue;}function calculateFactorial(number) {  if (validate(number, REQUIRED) && validate(number, MAX_LENGTH, 100) && validate(number, IS_TYPE, 'number')) {    return factorial(number);  } else {    throw new Error(      'Invalid input - either the number is to big or it is not a number'    );  }}function factorialHandler(event) {  event.preventDefault();  const inputNumber = getValueFromElement('#factorial');  try {    const result = calculateFactorial(inputNumber);    displayResult(result);  } catch (error) {    alert(error.message);  } }function addSubmitHandler(tag, handler) {  const form = getElement(tag);  form.addEventListener('submit', handler);}addSubmitHandler("#factorial-form", factorialHandler);

在这种办法中,咱们专门处理函数。每个函数都只有一个目标,大多数函数能够在程序的其余局部中重用。

对于这个简略的 Web 程序,应用函数式的办法有些过分了。接着将编写雷同的性能,只不过这次是面向对象的。

面向对象的实现

首先,须要将 index.html 文件的脚本标签中的 src 更改为以下内容。

<script src="oop.js" defer></script>

而后创立 oop.js 文件。

$ cat oop.js

对于面向对象办法,咱们要创立三种不同的类,一种用于验证,一种用于阶乘计算,另一种用于解决表单。

先是创立解决表单的类。

class InputForm {  constructor() {    this.form = document.getElementById('factorial-form');    this.numberInput = document.getElementById('factorial');    this.form.addEventListener('submit', this.factorialHandler.bind(this));  }  factorialHandler(event) {    event.preventDefault();    const number = this.numberInput.value;    if (!Validator.validate(number, Validator.REQUIRED)       || !Validator.validate(number, Validator.MAX_LENGTH, 100)      || !Validator.validate(number, Validator.IS_TYPE, 'number'))      {        alert('Invalid input - either the number is to big or it is not a number');        return;      }      const factorial = new Factorial(number);      factorial.display();  }}new InputForm();

在构造函数中获取 form-elementinput-element 并将其存储在类变量(也称为属性)中。之后将办法 factorialHandler 增加到 Submit-event 中。在这种状况下须要把类的 this 绑定到办法。如果不这样做,将会失去一个援用谬误,例如调用 this.numberInput.value 将会是 undefined。之后以事件为参数创立类办法 factorialHandler

该办法的代码看起来应该有点相熟,例如 if 语句查看输出值是否无效,就像在 calculateFactorial 函数中所做的那样。Validator.validate 是对咱们依然须要创立的 Validator 类中的静态方法的调用。如果应用静态方法,则无需初始化对象的新实例。验证通过后创立 Factorial 类的新实例,传递输出值,而后将计算的结果显示给用户。

接下来在 InputForm 类 后面创立 Validator 类。

class Validator {  static MAX_LENGTH = 'MAX_LENGTH';  static IS_TYPE = 'IS_TYPE';  static REQUIRED = 'REQUIRED';  static validate(value, flag, compareValue) {    switch (flag) {      case this.REQUIRED:        return value.trim().length > 0;      case this.MAX_LENGTH:        return value <= compareValue;      case this.IS_TYPE:        if (compareValue === 'number') {          return !isNaN(value);        } else if (compareValue === 'string') {          return isNaN(value);        }      default:        break;    }  }}

这个类外部的所有内容都是动态的,所以咱们不须要任何构造函数。

这样做的益处是不须要在每次应用它时都初始化该类。

validatevalidate 函数与咱们的 functional.js 简直完全相同。

接下来在 Validator 类的前面创立 Factorial 类。

class Factorial {  constructor(number) {    this.resultElement = document.getElementById('factorial-result');    this.number = number;    this.factorial = this.calculate();  }  calculate() {    let returnValue = 1;    for (let i = 2; i <= this.number; i++) {      returnValue = returnValue * i;    }    return returnValue;  }  display() {    this.resultElement.innerHTML = this.factorial;  }}

在初始化这个类的实例后,咱们取得 resultElement 并将其存储为属性以及咱们传入的数字。

之后调用办法 calculate 并将其返回值存储在属性中。calculate 办法蕴含与 functional.js 中的 factor 函数雷同的代码。最初是 display 办法,该办法将后果元素的 innerHTML 设置为事实计算出的阶乘数。

残缺的 oop.js 文件如下所示。

class Validator {  static MAX_LENGTH = 'MAX_LENGTH';  static IS_TYPE = 'IS_TYPE';  static REQUIRED = 'REQUIRED';  static validate(value, flag, compareValue) {    switch (flag) {      case this.REQUIRED:        return value.trim().length > 0;      case this.MAX_LENGTH:        return value <= compareValue;      case this.IS_TYPE:        if (compareValue === 'number') {          return !isNaN(value);        } else if (compareValue === 'string') {          return isNaN(value);        }      default:        break;    }  }}class Factorial {  constructor(number) {    this.resultElement = document.getElementById('factorial-result');    this.number = number;    this.factorial = this.calculate();  }  calculate() {    let returnValue = 1;    for (let i = 2; i <= this.number; i++) {      returnValue = returnValue * i;    }    return returnValue;  }  display() {    this.resultElement.innerHTML = this.factorial;  }}class InputForm {  constructor() {    this.form = document.getElementById('factorial-form');    this.numberInput = document.getElementById('factorial');    this.form.addEventListener('submit', this.factorialHandler.bind(this));  }  factorialHandler(event) {    event.preventDefault();    const number = this.numberInput.value;    if (!Validator.validate(number, Validator.REQUIRED)       || !Validator.validate(number, Validator.MAX_LENGTH, 100)      || !Validator.validate(number, Validator.IS_TYPE, 'number'))      {        alert('Invalid input - either the number is to big or it is not a number');        return;      }      const factorial = new Factorial(number);      factorial.display();  }}new InputForm();

咱们创立了三个类来处理程序的三个不同的性能:

  • 验证: Validation
  • 阶乘解决: Factorial
  • 表单解决: InputForm

总结

两种办法都是编写代码的无效办法。我喜爱在本人不同我的项目中尝试最无效的办法。在很多状况下,甚至不可能如此清晰地拆散这两种范式。

心愿这篇文章能够使你对不同的办法有一个根本的理解。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章

欢送持续浏览本专栏其它高赞文章:

  • 深刻了解Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13个帮你进步开发效率的古代CSS框架
  • 疾速上手BootstrapVue
  • JavaScript引擎是如何工作的?从调用栈到Promise你须要晓得的所有
  • WebSocket实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30分钟用Node.js构建一个API服务器
  • Javascript的对象拷贝
  • 程序员30岁前月薪达不到30K,该何去何从
  • 14个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把HTML转成PDF的4个计划及实现

  • 更多文章...