函数式 vs 面向对象 vs 过程式编程三种方法 JavaScript 示例

介绍

这是一个真实的例子,展示了三种最常见的编程范式之间的差异。我将以三种不同的方式解决一个问题。 它基于Academind Video ,但最终我的解决方案发生了一些变化。

 

每个示例将处理表单提交、验证用户输入并将创建的用户打印到控制台。我还添加了保存错误记录器。

html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- <script src="procedural.js" defer></script> -->
    <!-- <script src="oop.js" defer></script> -->
    <!-- <script src="functional.js" defer></script> -->
  </head>
  <body>
    <form id="user-form">
      <div>
        <label for="username">Username</label>
        <input id="username" />
      </div>
      <div>
        <label for="password">Password</label>
        <input id="password" type="password" />
      </div>
      <button type="submit">Submit</button>
    </form>
  </body>
</html>

简单的 HTML 登录表单,它将包含三个js不同范例的有效文件。

程序化编程

程序化编程只是一步一步地解决问题。这是一种完全有效的编码方式,但是当您希望应用程序扩展时,它有很多缺点。

const form = document.querySelector('form')
const logs = []

form.addEventListener('submit', e => {
  e.preventDefault()
  const username = e.target.elements.username.value
  const password = e.target.elements.password.value

  let error = ''

  if (username.trim().length < 3)
    error = 'Username must be at least 3 characters long'
  else if (!password.match(/[0-9]/))
    error = 'Password must contain at least one digit'

  if (error) {
    logs.push(error)
    alert(error)
    return
  }

  const user = {
    username,
    password,
  }

  console.log(user)
  console.log(logs)
})

简单的一步一步解决问题。但它根本不可重用和可扩展。尽管它对于解决此类问题是完全有效的,并且您会看到它比其他的要短得多。

面向对象编程

面向对象编程 ( OOP ) 是最接近现实世界的,因此很容易将您的想法包裹起来。我们查看将其划分为Object的代码,其中每个都只做它的工作。

在OOP中学习的有用概念是SOLID

// Class responsible only for logging
class Logger {
  static logs = []

  static showAlert(message) {
    this.logs.push(message)
    alert(message)
  }
}

// Class responsible only for validating input
class Validator {
  static flags = {
    minLength: 'MIN-LENGTH',
    hasDigit: 'HAS-DIGIT',
  }

  static validate(value, flag, validatorValue) {
    if (flag === this.flags.minLength) {
      return value.trim().length >= validatorValue
    }

    if (flag === this.flags.hasDigit) {
      return value.match(/[0-9]/)
    }
  }
}

// Class responsible only for creating valid user
class User {
  constructor(username, password) {
    if (!Validator.validate(username, Validator.flags.minLength, 3))
      throw new Error('Username must be at least 3 characters long')
    if (!Validator.validate(password, Validator.flags.hasDigit))
      throw new Error('Password must contain at least one digit')

    this.username = username
    this.password = password
  }
}

// Class responsible only for from handling
class FormHandler {
  constructor(formElement) {
    this.form = formElement
    this.form.addEventListener('submit', this.handleSubmit.bind(this))
  }

  handleSubmit(e) {
    e.preventDefault()
    const username = e.target.elements.username.value
    const password = e.target.elements.password.value

    try {
      const user = new User(username, password)
      console.log(user)
      console.log(Logger.logs)
    } catch (err) {
      Logger.showAlert(err)
    }
  }
}

const form = document.querySelector('form')
new FormHandler(form)

现在您可以看到我将问题划分为Objects的意思。FormHandler是它自己的类,负责处理表单。User是另一个负责创建用户并使用Validator类验证输入的类。如果出现错误Logger类用于显示警报并保存日志。

正如您所看到的,有更多的代码,而且看起来更复杂......那么为什么有人会喜欢这个而不是Procedura范式呢?

很酷的是,现在我们可以将它用于任何类似的形式,只需调用

new FormHandler(new_form)

因此,它可以在包含此脚本的每个文件中重复使用。而且它很容易扩展,因为一切都被分成只做一件事的块(单一责任原则)。

功能性

最后是我最喜欢的范式。在写这篇文章的时候它真的很受欢迎,而且非常直接。

请注意,这并不意味着它无论如何都更好。使用哪个
完全取决于您,尽管某些范例可能更适合某些问题。

const FLAGS = {
  minLength: 'MIN-LENGTH',
  hasDigit: 'HAS-DIGIT',
}

// Function that handles validation
const validate = (value, flag, validatorValue) => {
  switch(flag){
    case FLAGS.minLength:
      return value.trim().length >= validatorValue

    case FLAGS.hasDigit:
      return !!value.match(/[0-9]/)
  }
}

// Function that sets submit handler
const setFormSubmitHandler = (formId, onSubmit) => {
  const form = document.getElementById(formId)
  form.addEventListener('submit', onSubmit)
}

// Function that returns values of required fields as object
// In this case it will return {username: "<value>", password: "<value>"}
// It might look scary but keep in mind that it's completely reusable
const getFormValues = (e, ...fields) => {
  const values = Object.entries(e.target.elements)
  const filteredValues = values.filter(([key]) => fields.includes(key))
  return filteredValues.reduce(
    (acc, [key, { value }]) => ({ ...acc, [key]: value }),
    {}
  )
}

// Function that creates valid user
const createUser = (username, password) => {
  if (!validate(username, FLAGS.minLength, 3))
    throw new Error('Username must be at least 3 characters long')
  if (!validate(password, FLAGS.hasDigit))
    throw new Error('Password must contain at least one digit')

  return { username, password }
}

// Function that creates logger object with *logs* and *showAlert* function
const logger = (() => {
  const logs = []
  return {
    logs,
    showAlert: message => {
      logs.push(message)
      alert(message)
    },
  }
})()

// Main function
const handleSubmit = e => {
  e.preventDefault()
  const { username, password } = getFormValues(e, 'username', 'password')

  try {
    const user = createUser(username, password)
    console.log(user)
    console.log(logger.logs)
  } catch (error) {
    logger.showAlert(error)
  }
}

setFormSubmitHandler('user-form', handleSubmit)

正如您在函数式编程中看到的那样,我们希望使用小的(理想情况下是的)函数来解决问题。这种方法的可扩展性也很强,功能可以重复使用。

纯函数是一种没有难以跟踪的副作用的函数。
纯函数应该只依赖于给定的参数。

结论

没有更好和更差的范式。经验丰富的开发人员可以看到每个人的优势,并为给定的问题选择最好的。

过程式编程并不是说你不能使用函数,函数式编程也不会阻止你使用Class。这些范例只是帮助以一种随着代码增长而有益的方式解决问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值