HomeLập trìnhJavaScriptJavaScript 设计模式

JavaScript 设计模式



原文: Các mẫu thiết kế JavaScript – Được giải thích bằng các ví dụ,作者:German Cocca


译者: đu đủHUANG

大家好,在这篇文章中我将介绍设计模式是什么以及为什么很重要。

我还将介绍一些最流行的设计模式,并为每一种举例说明。让我们开始吧!

目录

设计 模式 个 一 《《对象 面向 面向 基础 基础 的 书 书 书 书 , , , , 年 个 个 工程师

这本书探讨了面向对象的编程的能力和陷阱,并介绍了23种可以用来解决编程问题的模式。

这些模式并不是算法或者具体的实现。它们更像是想法、观点和抽象,辅助你去解决一些特定问题。

根据要素的不同模式的实现也各不相同,重要的是模式背后的概念,它可以帮助我们好地耗闣墂

话 , , , , , , , 更 , , , 更 更 ,

不过把这些模式当作一般的编程知识来了解没有坏处。

旁注:如果你不熟悉编程范式或者OOP,推荐你阅读我最近写的这两篇文章。😉

设计模式的简介就到这里。设计模式可以被分为三大类:创建、结构、行为范例。让我们逐个了解。🧐

创建范例包括不同的创建对象的机制。

单例模式

单例模式确保 确保 确保 确保 , , , ,。 当 应用 能 能 能 能 就 能 能 就 就

比方说,我们想在一个单一对象中包含应用程序进行任何复制仿

可以通过对象字面量和类者两种方法来实现:

const Config = {
  start: () => console.log('App has started'),
  update: () => console.log('App has updated'),
}

// 通过冻结对象来限制增加新的属性或者修改已有属性
Object.freeze(Config)

Config.start() // "App has started"
Config.update() // "App has updated"

Config.name = "Robert" // 尝试添加一个新的键
console.log(Config) // 添加失败: { start: [Function: start], update: [Function: update] }

使用对象的字面量

class Config {
    constructor() {}
    start(){ console.log('App has started') }  
    update(){ console.log('App has updated') }
}
  
const instance = new Config()
Object.freeze(instance)

使用类

工厂方法

工厂方法提供 提供 提供 提供 , , , , 逻辑 逻辑 逻辑 集中 集中 , , 代码 代码 代码 代码 代码 代码 代码 代码 代码 更易 更易 更易 更易 更易 更易 更易 更易 更易

这种模式被大量应用。可以通过类和工厂函数(返回对象的函数)来实现:

class Alien {
    constructor (name, phrase) {
        this.name = name
        this.phrase = phrase
        this.species = "alien"
    }
    fly = () => console.log("Zzzzzziiiiiinnnnnggggg!!")
    sayPhrase = () => console.log(this.phrase)
}

const alien1 = new Alien("Ali", "I'm Ali the alien!")
console.log(alien1.name) // 输出:"Ali"

使用类

function Alien(name, phrase) {
    this.name = name
    this.phrase = phrase
    this.species = "alien"
}

Alien.prototype.fly = () => console.log("Zzzzzziiiiiinnnnnggggg!!")
Alien.prototype.sayPhrase = () => console.log(this.phrase)

const alien1 = new Alien("Ali", "I'm Ali the alien!")

console.log(alien1.name) // 输出 "Ali"
console.log(alien1.phrase) // 输出 "I'm Ali the alien!"
alien1.fly() // 输出 "Zzzzzziiiiiinnnnnggggg"

使用工厂函数

抽象工厂

抽象工厂允许 允许 允许 允许 你 创建 共 共 共 享 , , , , 可以 可以 可以 可以 可以 可以 可以 可以 可以 可以 派上 派上 派上 派上 派上 派上

Bạn có thể làm điều đó với tôi không?抽象工厂通过特定逻辑调用具体工厂,具体工厂返回最终的对象。

这样做给工厂模式添加了一个抽象层,我们通过仅和单个工厂函数或者类交互来创建各种不幱籚

让我们来看几个例子。假设我们是汽车公司,我们除了生产小汽车以外,还生产摩托车和卡车」

// 每个汽车种类有一个类或者“具体工厂”
class Car {
    constructor () {
        this.name = "Car"
        this.wheels = 4
    }
    turnOn = () => console.log("Chacabúm!!")
}

class Truck {
    constructor () {
        this.name = "Truck"
        this.wheels = 8
    }
    turnOn = () => console.log("RRRRRRRRUUUUUUUUUMMMMMMMMMM!!")
}

class Motorcycle {
    constructor () {
        this.name = "Motorcycle"
        this.wheels = 2
    }
    turnOn = () => console.log("sssssssssssssssssssssssssssssshhhhhhhhhhham!!")
}

// 抽象工厂作为单一交互点和客户端交互
// 接受特定汽车类型作为参数,调用对应类型的具体工厂
const vehicleFactory = {
    createVehicle: function (type) {
        switch (type) {
            case "car":
                return new Car()
            case "truck":
                return new Truck()
            case "motorcycle":
                return new Motorcycle()
            default:
                return null
        }
    }
}

const car = vehicleFactory.createVehicle("car") // Car { turnOn: [Function: turnOn], name: 'Car', wheels: 4 }
const truck = vehicleFactory.createVehicle("truck") // Truck { turnOn: [Function: turnOn], name: 'Truck', wheels: 8 }
const motorcycle = vehicleFactory.createVehicle("motorcycle") // Motorcycle { turnOn: [Function: turnOn], name: 'Motorcycle', wheels: 2 }

构造器

构造器Bạn có thể theo dõi “Trò chơi” của bạn không?

Đọc thêm  ¿Có moipular arreglos en JavaScript không?

构造器的好处在于通过不同实体分开创建属性和方法。

通过 类 构造 , , , , , , , , , , , 可以 只 只

这个概念和对象组合相关, 我在这篇文章讨论过这个话题。

// 声明一个对象
const bug1 = {
    name: "Buggy McFly",
    phrase: "Your debugger doesn't work with me!"
}

const bug2 = {
    name: "Martiniano Buggland",
    phrase: "Can't touch this! Na na na na..."
}

// 这些函数将对象作为参数,并为对象添加方法
const addFlyingAbility = obj => {
    obj.fly = () => console.log(`Now ${obj.name} can fly!`)
}

const addSpeechAbility = obj => {
    obj.saySmthg = () => console.log(`${obj.name} walks the walk and talks the talk!`)
}

// 最后传入对象作为参数,调用构造器函数
addFlyingAbility(bug1)
bug1.fly() // 输出: "Now Buggy McFly can fly!"

addSpeechAbility(bug2)
bug2.saySmthg() // 输出: "Martiniano Buggland walks the walk and talks the talk!"

原型

原型Bạn sẽ không bao giờ làm được điều đó nếu bạn không muốn làm điều đó.

Bạn có thể sử dụng JavaScript để phát triển JavaScript không?

原型链继承的结果和使用类相似,只是更为灵活,因为属性和方法可以不通过同一个类在对藴层

// 声明一个有两个方法的原型对象
const enemy = {
    attack: () => console.log("Pim Pam Pum!"),
    flyAway: () => console.log("Flyyyy like an eagle!")
}

// 声明另外一个对象,这个对象将继承原型
const bug1 = {
    name: "Buggy McFly",
    phrase: "Your debugger doesn't work with me!"
}

// 使用setPrototypeOf设置对象的原型
Object.setPrototypeOf(bug1, enemy)

// 使用getPrototypeOf来确认我们是否设置成功
console.log(Object.getPrototypeOf(bug1)) // { attack: [Function: attack], flyAway: [Function: flyAway] }

console.log(bug1.phrase) // Your debugger doesn't work with me!
console.log(bug1.attack()) // Pim Pam Pum!
console.log(bug1.flyAway()) // Flyyyy like an eagle!

结构范例将对象和类组合成更大的结构。

适配器

适配器允许两个接口不兼容的对象相互交互。

假设 假设 假设 假设 假设 假设 , , , , , 来 , 但是 能 直接 直接 能 能 能 能 能 能 能 能 能 能 能先适配结果。 😉

我们 我们 我们 我们 我们 有 , , 一 拥有 但是 但是 但是 但是 但是 但是 但是 但是 单位 单位 单位 单位 单位 单位一个新城市的人口单位不是百万:

// 城市数组
const citiesHabitantsInMillions = [
    { city: "London", habitants: 8.9 },
    { city: "Rome", habitants: 2.8 },
    { city: "New york", habitants: 8.8 },
    { city: "Paris", habitants: 2.1 },
] 

// 待添加的新城市
const BuenosAires = {
    city: "Buenos Aires",
    habitants: 3100000
}

// 适配器函数将城市的人口属性转换成统一的计数单位
const toMillionsAdapter = city => { city.habitants = parseFloat((city.habitants/1000000).toFixed(1)) }

toMillionsAdapter(BuenosAires)

// 将新城市添加到数组
citiesHabitantsInMillions.push(BuenosAires)

// 函数返回人口最多的城市
const MostHabitantsInMillions = () => {
    return Math.max(...citiesHabitantsInMillions.map(city => city.habitants))
}

console.log(MostHabitantsInMillions()) // 8.9

装饰

装饰通过 增加 修饰 , , , , , , 行为 行为 如果 如果 如果 如果 你 熟悉 熟悉 熟悉 铃铛 铃铛 铃铛 铃铛 铃铛 铃铛 铃铛 铃铛 铃铛

从 技术 讲 , 中 的 组件 函数 如果 如果 思索 思索 思索 思索 思索 思索 思索 上下文 上下文 上下文 ((我们 , 子组件 子组件 子组件 子组件可以访问某些功能。

Bạn có thể sử dụng ContextProvider để hỗ trợ:


import { useState } from 'react'
import Context from './Context'

const ContextProvider: React.FC = ({children}) => {

    const [darkModeOn, setDarkModeOn] = useState(true)
    const [englishLanguage, setEnglishLanguage] = useState(true)

    return (
        <Context.Provider value={{
            darkModeOn,
            setDarkModeOn,
            englishLanguage,
            setEnglishLanguage
        }} >
            {children}
        </Context.Provider>
    )
}

export default ContextProvider

然后我们包裹整个应用:

export default function App() {
  return (
    <ContextProvider>
      <Router>

        <ErrorBoundary>
          <Suspense fallback={<></>}>
            <Header />
          </Suspense>

          <Routes>
              <Route path="/" element={<Suspense fallback={<></>}><AboutPage /></Suspense>}/>

              <Route path="/projects" element={<Suspense fallback={<></>}><ProjectsPage /></Suspense>}/>

              <Route path="/projects/helpr" element={<Suspense fallback={<></>}><HelprProject /></Suspense>}/>

              <Route path="/projects/myWebsite" element={<Suspense fallback={<></>}><MyWebsiteProject /></Suspense>}/>

              <Route path="/projects/mixr" element={<Suspense fallback={<></>}><MixrProject /></Suspense>}/>

              <Route path="/projects/shortr" element={<Suspense fallback={<></>}><ShortrProject /></Suspense>}/>

              <Route path="/curriculum" element={<Suspense fallback={<></>}><CurriculumPage /></Suspense>}/>

              <Route path="/blog" element={<Suspense fallback={<></>}><BlogPage /></Suspense>}/>

              <Route path="/contact" element={<Suspense fallback={<></>}><ContactPage /></Suspense>}/>
          </Routes>
        </ErrorBoundary>

      </Router>
    </ContextProvider>
  )
}

接着,我们使用useContext钩子,使得应用内所有组件都可以获得定义在Context的状态(state):


const AboutPage: React.FC = () => {

    const { darkModeOn, englishLanguage } = useContext(Context)
    
    return (...)
}

export default AboutPage

这 个 可能 不 的 作者 这个 模式 想到 确切 确切 , , , , , 我 想法 想法 是 : : : :

Đọc thêm  Cách phát hiện bảng màu ưa thích của người dùng trong JavaScript

外观

外观模式给库、框架以及其他复杂类集提供简化的接口。

嗯 嗯 我们 举 , , , 以及 以及 以及 以及 开发 开发 开发 开发 开发 开发 的 的对开发者隐藏复杂性。

JavaScript mapsortreducefilter函数都是很好的例子,这些函数的背后其实是我们的老朋友for循环。

另 一 例子 是 一些 库 , , , , , , , , , , , , , , , , ,

这些 这些 这些 这些 简单 的 的 的 东西 东西 东西 组件 , , , , , 过程 过程 过程 过程 更 更 更 更 更 更 更 更 更 更 更 更

thewolfofwallstreet-fairydust

一个外观模式……

import * as React from 'react';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';

function createData(
  name: string,
  calories: number,
  fat: number,
  carbs: number,
  protein: number,
) {
  return { name, calories, fat, carbs, protein };
}

const rows = [
  createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
  createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
  createData('Eclair', 262, 16.0, 24, 6.0),
  createData('Cupcake', 305, 3.7, 67, 4.3),
  createData('Gingerbread', 356, 16.0, 49, 3.9),
];

export default function BasicTable() {
  return (
    <TableContainer component={Paper}>
      <Table sx={{ minWidth: 650 }} aria-label="simple table">
        <TableHead>
          <TableRow>
            <TableCell>Dessert (100g serving)</TableCell>
            <TableCell align="right">Calories</TableCell>
            <TableCell align="right">Fat&nbsp;(g)</TableCell>
            <TableCell align="right">Carbs&nbsp;(g)</TableCell>
            <TableCell align="right">Protein&nbsp;(g)</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map((row) => (
            <TableRow
              key={row.name}
              sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
            >
              <TableCell component="th" scope="row">
                {row.name}
              </TableCell>
              <TableCell align="right">{row.calories}</TableCell>
              <TableCell align="right">{row.fat}</TableCell>
              <TableCell align="right">{row.carbs}</TableCell>
              <TableCell align="right">{row.protein}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

代理

代理模式 模式 模式 替代 原始 , , , , , , , , 请求 请求 请求 请求 操作 操作

如果 你 熟悉 expressjs 的话 , 个 就 不 陌生 是 是 用 是 中间件 使用。。 一 一 一 一 一 一 一 一 一 一 一 一 一 一 一 一。

让 让 一 , , , , , , , , 是 , , , , , , , , , , , ,next()函数。

const jwt = require('jsonwebtoken')

module.exports = function authenticateToken(req, res, next) {
    const authHeader = req.headers['authorization']
    const token = authHeader && authHeader.split(' ')[1]
  
    if (token === null) return res.status(401).send(JSON.stringify('No access token provided'))
  
    jwt.verify(token, process.env.TOKEN_SECRET, (err, user) => {
      if (err) return res.status(403).send(JSON.stringify('Wrong token provided'))
      req.user = user
      next()
    })
}

这个 就 一 , , 可以 可以 中 中 终点 使用 中间件 只 需要 需要 将 其 添加 在 地址 之后 之后 之后 之后 之后 之后 之后 终点 终点 的 之前 之前 之前 :

router.get('/:jobRecordId', authenticateToken, async (req, res) => {
  try {
    const job = await JobRecord.findOne({_id: req.params.jobRecordId})
    res.status(200).send(job)

  } catch (err) {
    res.status(500).json(err)
  }
})

如果 如果 如果 , , , 响 应 应 应 应 应 应 应 如果 , , , , ,next()函数,然后将执行终点函数。

Đọc thêm  Hướng dẫn cơ bản về JavaScript Date và Moment.js

我们 我们 我们 我们 , , , , , , , , 但 使用 使用 中间件 复用 复用 复用 复用 复用

同样 这个 不 , , , , 这 一 一 例子 例子 例子。 我们 我们 控制 对象 的 , , , , ,

行为范式控制不同对象之间的通讯。

责任链

责任链将 请求 , , , , , , , , , , , , , , 链条 链条

我们 可以 示例 这个 , , 因为 因为 的 的 就 是 一 处理 , , , 要么 , , , 将 其 其 其 传递 传递 下 个 处理 程序 程序 程序

如果 如果 如果 如果 如果 一 步 中 , , 实体 会 会 会 会 会 会 会 会 会 会 会 会 会 会一个实体。

Bạn có nên sử dụng API cho các ứng dụng API không?

  • 有一个负责渲染UI的函数
  • 一旦渲染,另一个函数向API终点发出请求
  • 如果终点响应符合预期,则将信息传递给另一个函数,该函数以给定方式对数据进行鎒序嘶孨學参
  • 一旦变量存储了所需的信息,另一个函数负责在UI中呈现它。

可以 看到 许多 实体 执行 任务 个 个 个 个 个 , , ,

迭代器

迭代器用于遍历集合的元素。这在现代编程语言中显得微不足道,但并非如此。

Công cụ JavaScriptforforEachfor...offor...inmapreducefilter等)就是手边可以拿来遍历数据结构的方法。

遍历算法 以及更为复杂的树和图这样的数据结使用的代码是迭代器的例子。

观察者

观察者模式 模式 一 机制 机制 对象 的 的 的 任何 , , , , 给定 一 一 一 一 一 一 一时,我们会采取一些行动。

React và useEffect không được khuyến khích sử dụng bởi bất kỳ ai.

钩子 分为 主要 : 可 函数 依赖 数 数 , , , , , 下 例 例 , , , , , , ,

  useEffect(() => { console.log('The component has rendered') }, [])

如果在依赖数组中声明任何变量,则该函数将仅些变量发生变化时执行。

  useEffect(() => { console.log('var1 has changed') }, [var1])

也 可以 JavaScript 的 监听器 视为 模式 , , 编程 库如 库如 库如 库如 , , , 处理 异步 信息 事件 的 的

如果你想了解更多相关信息,推荐观看这个视频 或访问这个站,你可以找到每个模缏的配囆軻站

希望你享受阅读这篇文章,并有所收获。你可以在LinkedIn thành lậpTwitter上关注我。

干杯!我们下篇文章见! ✌️

Xem-ya-GIF





Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status