JavaScript 基础教程

JavaScript 首页 JavaScript 路线图 JavaScript 概述 JavaScript 功能特性 JavaScript 启用 JavaScript 位置 JavaScript 语法 JavaScript Hello World JavaScript Console.log() JavaScript 注释 JavaScript 变量 JavaScript let 语句 JavaScript 常量 JavaScript 数据类型 JavaScript 类型转换 JavaScript 严格模式 JavaScript 保留关键字

JavaScript 运算符

JavaScript 运算符 JavaScript 算术运算符 JavaScript 比较运算符 JavaScript 逻辑运算符 JavaScript 位运算符 JavaScript 赋值运算符 JavaScript 条件运算符 JavaScript typeof 运算符 JavaScript 空值合并运算符 JavaScript 安全赋值运算符 JavaScript 删除运算符 JavaScript 逗号运算符 JavaScript 分组运算符 JavaScript Yield 运算符 JavaScript 展开运算符 JavaScript 幂运算符 JavaScript 运算符优先级

JavaScript 控制流

JavaScript If...Else JavaScript While 循环 JavaScript For 循环 JavaScript For...in JavaScript For...of JavaScript 循环控制 JavaScript Break 语句 JavaScript Continue 语句 JavaScript Switch Case JavaScript 用户定义迭代器

JavaScript 函数

JavaScript 函数 JavaScript 函数表达式 JavaScript 函数参数 JavaScript 默认参数 JavaScript Function() 构造函数 JavaScript 函数提升 JavaScript 自调用函数 JavaScript 箭头函数 JavaScript 函数调用 JavaScript 函数 call() 方法 JavaScript 函数 apply() 方法 JavaScript 函数 bind() 方法 JavaScript 闭包 JavaScript 变量作用域 JavaScript 全局变量 JavaScript 智能函数参数

JavaScript 对象

JavaScript Number JavaScript Boolean JavaScript Strings JavaScript Arrays JavaScript Date JavaScript DataView JavaScript Handler JavaScript Math JavaScript RegExp JavaScript Symbol JavaScript Sets JavaScript WeakSet JavaScript Maps JavaScript WeakMap JavaScript 可迭代对象 JavaScript Reflect JavaScript TypedArray JavaScript 模板字面量 JavaScript 带标签的模板

面向对象的 JavaScript

JavaScript 对象 JavaScript 类 JavaScript 对象属性 JavaScript 对象方法 JavaScript 静态方法 JavaScript 显示对象 JavaScript 对象访问器 JavaScript 对象构造函数 JavaScript 原生原型 JavaScript ES5 对象方法 JavaScript 封装 JavaScript 继承 JavaScript 抽象 JavaScript 多态 JavaScript 解构 JavaScript 解构赋值 JavaScript 对象解构 JavaScript 数组解构 JavaScript 嵌套解构 JavaScript 可选链式调用 JavaScript 全局对象 JavaScript Mixins (混合) JavaScript 代理

JavaScript 版本

JavaScript 历史 JavaScript 版本 JavaScript ES5 JavaScript ES6 ECMAScript 2016 ECMAScript 2017 ECMAScript 2018 ECMAScript 2019 ECMAScript 2020 ECMAScript 2021 ECMAScript 2022

JavaScript 异步

JavaScript 异步 JavaScript 回调函数 JavaScript Promises JavaScript Async/Await JavaScript Microtasks (微任务) JavaScript Promises JavaScript Promises 链 JavaScript 定时事件 JavaScript setTimeout() JavaScript setInterval()

JavaScript Cookies

JavaScript Cookies JavaScript Cookie 属性 JavaScript 删除 Cookies

JavaScript 浏览器 BOM

JavaScript 浏览器对象模型 JavaScript Window 对象 JavaScript Document 文档对象 JavaScript Screen 对象 JavaScript History 对象 JavaScript Navigator 对象 JavaScript Location 对象 JavaScript Console 对象

JavaScript Web APIs

JavaScript Web API JavaScript History API JavaScript Storage API JavaScript Forms API JavaScript Worker API JavaScript Fetch API JavaScript Geolocation API

JavaScript 事件

JavaScript 事件 JavaScript DOM 事件 JavaScript addEventListener() JavaScript 鼠标事件 JavaScript 键盘事件 JavaScript 表单事件 JavaScript 窗口/文档事件 JavaScript 事件委托 JavaScript 事件冒泡 JavaScript 事件捕获 JavaScript 自定义事件

JavaScript 错误处理

JavaScript 错误处理 JavaScript try...catch JavaScript 调试 JavaScript 自定义错误 JavaScript 扩展错误

JavaScript 重要关键字

JavaScript this 关键字 JavaScript void 关键字 JavaScript new 关键字 JavaScript var 关键字

JavaScript HTML DOM

JavaScript HTML DOM JavaScript DOM 方法 &属性 JavaScript DOM 文档 JavaScript DOM 元素 JavaScript DOM 属性 (Attr) JavaScript DOM 表单 JavaScript 修改 HTML JavaScript 修改 CSS JavaScript DOM 动画 JavaScript DOM 导航 JavaScript DOM 集合 JavaScript DOM NodeList JavaScript DOM DOMTokenList

JavaScript 高级章节

JavaScript 冒泡排序算法 JavaScript 循环引用错误 JavaScript 使用 Jest 进行代码测试 JavaScript CORS 处理 JavaScript 数据分析 JavaScript 死区 JavaScript 设计模式 JavaScript Engine 和 Runtime JavaScript 执行上下文 JavaScript 函数组合 JavaScript 不可变性 JavaScript Kaboom.js JavaScript 词法作用域 JavaScript 本地存储 JavaScript 记忆化 JavaScript 压缩 JS JavaScript 可变性 vs 不可变性 JavaScript 包管理器 JavaScript 解析 S 表达式 JavaScript 原型继承 JavaScript 响应式 JavaScript Require 函数 JavaScript Selection API JavaScript SessionStorage JavaScript SQL CRUD 操作 JavaScript 增强排序 JavaScript 临时死区 JavaScript 节流 JavaScript TRPC 库 JavaScript 真值和假值 JavaScript 上传文件 JavaScript 日期比较 JavaScript 递归 JavaScript 数据结构 JavaScript Base64 编码 JavaScript 回调函数 JavaScript 当前日期/时间 JavaScript 日期验证 JavaScript 过滤方法 JavaScript 生成颜色 JavaScript HTTP 请求 JavaScript 插入排序 JavaScript 延迟加载 JavaScript 链表 JavaScript 嵌套循环 JavaScript 空值检查 JavaScript 获取当前 URL JavaScript 图算法 JavaScript 高阶函数 JavaScript 空字符串检查 JavaScript 表单处理 JavaScript 函数式编程 JavaScript 形参 vs 实参 JavaScript 原型 JavaScript 响应式编程 JavaScript Reduce 方法 JavaScript Rest 运算符 JavaScript 短路 JavaScript 未定义检查 JavaScript 单元测试 JavaScript 验证 URL

JavaScript 杂项

JavaScript Ajax JavaScript 异步迭代 JavaScript Atomics 原子对象 JavaScript Rest 参数 JavaScript 页面重定向 JavaScript 对话框 JavaScript 页面打印 JavaScript 表单验证 JavaScript 动画 JavaScript 多媒体 JavaScript 图像映射 JavaScript 浏览器 JavaScript JSON JavaScript 多行字符串 JavaScript 日期格式 JavaScript 获取日期方法 JavaScript 设置日期方法 JavaScript 模块 JavaScript 动态导入 JavaScript BigInt JavaScript Blob JavaScript Unicode JavaScript 浅拷贝 JavaScript 调用堆栈 JavaScript 引用类型 JavaScript IndexedDB JavaScript 点击劫持攻击 JavaScript 柯里化 JavaScript 图形 JavaScript Canvas 画布 JavaScript 防抖 JavaScript 性能 JavaScript 代码风格指南

JavaScript 实用资源

JavaScript 面试题 JavaScript 速查表 JavaScript 函数


JavaScript - 设计模式

在 JavaScript 中,设计模式是指用于解决特定环境下的一般设计问题的类和通信对象。针对软件设计和开发过程中出现的典型问题,提供通用且可重用的解决方案,被称为软件设计模式。

它们为开发者提供了一个讨论成功设计理念的平台,并作为解决特定类型问题的最佳实践。

什么是设计模式?

软件工程中的设计模式是针对程序设计中典型问题的通用且可复制的解决方案。设计尚未完善,无法立即进行编码。它是一种可用于各种场景的问题解决描述或模型。

软件设计模式的类型

在 JavaScript 中,主要有三类设计模式,分别是:-

  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式

让我们逐一讨论一下 -

JavaScript 中的创建型设计模式

在软件开发中,创建型设计模式是设计模式的一个子集。它们作用于对象生成过程,使其更加灵活高效。它维护了系统及其对象的组成、表示和创建的独立性。

单例模式

单例设计模式确保一个类只有一个不可变的实例。简单来说,单例模式是指一个对象无法被更改或复制。当我们希望应用程序拥有一个不可变的单一真实数据点时,它通常很有用。

假设我们想要一个包含应用程序所有配置的对象。我们还希望禁止复制或修改该对象。

实现此模式的两种方法是使用类和对象字面量 -

const Settings = {
  initialize: () => console.log('Application is now running'),
  refresh: () => console.log('Application data has been refreshed'),
}

// 冻结对象以防止修改
Object.freeze(Settings)

// "应用程序正在运行"
Settings.initialize()

// "应用程序数据已刷新"
Settings.refresh()

// 尝试添加新键
Settings.version = "1.0"
console.log(Settings)

这将生成以下结果 -

Application is now running
Application data has been refreshed
{ initialize: [Function: initialize], refresh: [Function: refresh] }
class Settings {
   constructor() {}
   launch() { console.log('Application is now running') }  
   refresh() { console.log('Application data has been refreshed') }
}
const appInstance = new Settings()
Object.freeze(appInstance)

// "应用程序正在运行"
appInstance.launch()

// "应用程序数据已刷新"
appInstance.refresh()

输出

以上代码的结果如下 -

Application is now running
Application data has been refreshed

工厂方法模式

工厂方法模式提供了一个用于创建对象的接口,这些对象在生成后可以进行修改。通过将构建对象的逻辑集中到一个地方,这可以简化并增强代码的组织结构。

这种常用的模式可以通过两种方式实现:使用类或工厂函数(即返回对象的方法)。

class Creature {
   constructor(name, message) {
      this.name = name
      this.message = message
      this.type = "creature"
   }
   fly = () => console.log("Whoooosh!!")
   speak = () => console.log(this.message)
}
const creature1 = new Creature("Zee", "Hello! I'm Zee from outer space!")
console.log(creature1.name) // output: "Zee"

这将产生以下输出 -

Zee

抽象工厂模式

无需了解链接对象的确切类型,我们可以使用抽象工厂模式来生成一组对象。当我们需要构建仅具有少数共同特征的独特对象时,这种方法非常有用。

它的工作原理如下:一个主抽象工厂连接客户端或用户。然后,该抽象工厂使用某种逻辑调用特定工厂来生成实际对象。因此,它充当了典型工厂布局的覆盖层,使我们能够在一个主工厂中生产各种产品。

例如,我们可以为一家汽车制造商开发一个包含卡车、摩托车和轿车的系统。抽象工厂模式可以通过一个主工厂处理多种对象类型,从而让我们能够轻松地做到这一点。

// 每个类代表一种特定类型的车辆
class Car {
   constructor () {
      this.type = "Car"
      this.wheels = 4
   }
   startEngine = () => console.log("Vroom Vroom!")
}
class Truck {
   constructor () {
      this.type = "Truck"
      this.wheels = 8
   }
   startEngine = () => console.log("Rumble Rumble!")
}
class Motorcycle {
   constructor () {
      this.type = "Motorcycle"
      this.wheels = 2
   }
   startEngine = () => console.log("Zoom Zoom!")
}
const vehicleFactory = {
   createVehicle: function (vehicleType) {
      switch (vehicleType) {
         case "car":
            return new Car()
         case "truck":
            return new Truck()
         case "motorcycle":
            return new Motorcycle()
         default:
            return null
      }
   }
}

const car = vehicleFactory.createVehicle("car") 
const truck = vehicleFactory.createVehicle("truck") 
const motorcycle = vehicleFactory.createVehicle("motorcycle") 

console.log(car.type)
car.startEngine()

console.log(truck.type) 
truck.startEngine() 

console.log(motorcycle.type)
motorcycle.startEngine() 

输出

这将生成以下结果 -

Car
Vroom Vroom!
Truck
Rumble Rumble!
Motorcycle
Zoom Zoom!

Builder 建造者模式

建造者模式用于分步生成对象。用于添加特定属性或方法的函数或方法通常包含在对象中。

由于它将方法和属性的开发分离为单独的实体,这种设计非常巧妙。

我们创建的对象始终包含由类或工厂函数定义的所有属性和方法。建造者模式使我们能够构造对象并仅执行所需的"步骤",从而提供了一种更灵活的方法。

// 定义我们的生物
const creature1 = {
   name: "Buzz Lightwing",
   message: "You'll never debug me!"
}
const creature2 = {
   name: "Sneaky Bugsworth",
   message: "Can't catch me! Ha ha!"
}
// 这些函数为对象添加能力
const enableFlying = obj => {
   obj.fly = () => console.log(`${obj.name} has taken flight!`)
}
const enableSpeaking = obj => {
   obj.speak = () => console.log(`${obj.name} says: "${obj.message}"`)
}
// 为生物添加能力
enableFlying(creature1)
creature1.fly()

enableSpeaking(creature2)
creature2.speak()

输出

这将给出以下结果 -

Buzz Lightwing has taken flight!
Sneaky Bugsworth says: "Can't catch me! Ha ha!"

Prototype 原型模式

通过使用另一个对象作为蓝图并继承其属性和方法,您可以使用原型模式创建对象。

如果您使用过 JavaScript,那么您可能对原型继承及其处理方式很熟悉。

由于方法和属性可以在对象之间共享,而无需依赖于同一个类,因此最终结果比使用类时更加灵活。

// 定义原型对象
const creatureAbilities = {
   attack: () => console.log("Zap! Zap!"),
   escape: () => console.log("Soaring through the sky!")
}
// 创建新生物
const creature1 = {
   name: "Winged Buzz",
   message: "You can't debug me!"
}
// 设置原型
Object.setPrototypeOf(creature1, creatureAbilities)

// 确认原型设置成功
console.log(Object.getPrototypeOf(creature1)) 

console.log(creature1.message) 
console.log(creature1.attack()) 
console.log(creature1.escape()) 

输出

这将产生以下结果 -

{ attack: [Function: attack], escape: [Function: escape] }
You can't debug me!
Zap! Zap!
undefined
Soaring through the sky!
undefined

JavaScript 中的结构化设计模式

在软件开发中,结构化设计模式是设计模式的一个子集,它关注如何将类或对象组合在一起以创建更复杂、更大的结构。为了提高软件系统的灵活性、可重用性和可维护性,它们有助于组织和管理对象之间的关系。

适配器模式

适配器方法是一种结构化设计模式,它可以帮助您连接两个不兼容接口之间的差距,并使它们协同工作。

让我们看下面的示例 -

// 人口以百万为单位的城市数组
const citiesPopulationInMillions = [
   { city: "Mumbai", population: 12.5 },
   { city: "Delhi", population: 19.0 },
   { city: "Bangalore", population: 8.4 },
   { city: "Kolkata", population: 4.5 },
]

// 我们要添加的新城市
const Chennai = {
   city: "Chennai",
   population: 7000000
}

const toMillionsAdapter = city => { city.population = parseFloat((city.population / 1000000).toFixed(1)) }

toMillionsAdapter(Chennai)

// 我们将新城市添加到数组中
citiesPopulationInMillions.push(Chennai)

// 此函数返回最大人口数 
const CityWithMostPopulation = () => {
   return Math.max(...citiesPopulationInMillions.map(city => city.population))
}

console.log(CityWithMostPopulation()) 

输出

这将生成以下结果 -

19

Decorator 装饰器模式

装饰器方法是一种结构化设计模式,它允许向特定对象添加静态或动态功能,而无需改变同一类中其他对象的行为。

以下是此反应性模式的简单演示 -

// 汽车的基类
class Car {
   constructor() {
     this.description = "Basic Car";
   }

   getDescription() {
     return this.description;
   }

   cost() {
     // Base price  
     return 10000; 
   }
}

// 装饰器添加空调
class AirConditioningDecorator {
   constructor(car) {
     this.car = car;
   }

   getDescription() {
     return this.car.getDescription() + " + Air Conditioning";
   }

   cost() {
    // 增加空调成本
    return this.car.cost() + 1500; 
   }
}

// 装饰器添加天窗
class SunroofDecorator {
   constructor(car) {
     this.car = car;
   }

   getDescription() {
     return this.car.getDescription() + " + Sunroof";
   }

   cost() {
     return this.car.cost() + 2000; 
   }
}

// 创建一辆基本汽车
const myCar = new Car();
console.log(myCar.getDescription());
console.log(`Cost: $${myCar.cost()}`);

// 添加空调功能
const myCarWithAC = new AirConditioningDecorator(myCar);
console.log(myCarWithAC.getDescription());
console.log(`Cost: $${myCarWithAC.cost()}`);

// 为汽车添加天窗功能
const myCarWithACAndSunroof = new SunroofDecorator(myCarWithAC);
console.log(myCarWithACAndSunroof.getDescription()); 
console.log(`Cost: $${myCarWithACAndSunroof.cost()}`); 

输出

这将生成以下结果 -

Basic Car
Cost: $10000
Basic Car + Air Conditioning
Cost: $11500
Basic Car + Air Conditioning + Sunroof
Cost: $13500

Facade 外观模式

外观方法是一种结构化设计模式,它通过为子系统中的一组接口提供更高级别、更简化的接口来改进客户端与子系统的交互。

为了更轻松地与包含汽车中多个复杂部件的子系统进行交互,我们需要创建一个 CarFacade 类。客户端应该只使用 CarFacade 类,而不应该使用单个组件。

// 子系统类
class Engine {
   start() {
     console.log("Engine started!");
   }

   stop() {
     console.log("Engine stopped!");
   }
}
class Transmission {
   shiftGear() {
     console.log("Transmission shifted!");
   }
}
class AirConditioning {
   turnOn() {
     console.log("Air conditioning is ON.");
   }

   turnOff() {
     console.log("Air conditioning is OFF.");
   }
}
// Facade class
class CarFacade {
   constructor() {
     this.engine = new Engine();
     this.transmission = new Transmission();
     this.airConditioning = new AirConditioning();
   }
   startCar() {
     console.log("Starting the car...");
     this.engine.start();
     this.transmission.shiftGear();
     this.airConditioning.turnOn();
   }
   stopCar() {
     console.log("Stopping the car...");
     this.airConditioning.turnOff();
     this.transmission.shiftGear();
     this.engine.stop();
   }
}
// 使用 Facade 模式的客户端代码
const myCar = new CarFacade();

// 启动汽车
myCar.startCar();

// 停止汽车
myCar.stopCar();

输出

这将生成以下结果 -

Starting the car...
Engine started!
Transmission shifted!
Air conditioning is ON.
Stopping the car...
Air conditioning is OFF.
Transmission shifted!
Engine stopped!

Proxy 代理模式

代理方法是一种结构化设计模式,它允许您创建一个对象替代品,该替代品可以控制对真实对象的访问,或充当中间件。

// 真实对象
class RealImage {
   constructor(filename) {
     this.filename = filename;
     this.load();
   }
   load() {
     console.log(`Loading image: ${this.filename}`);
   }
   display() {
     console.log(`Displaying image: ${this.filename}`);
   }
}
// Proxy class
class ProxyImage {
   constructor(filename) {
     this.filename = filename;
     // 仅在需要时加载真实图像
     this.realImage = null;  
   }
   load() {
     if (!this.realImage) {
       // 延迟加载
       this.realImage = new RealImage(this.filename);  
     }
   }
   display() {
     // 确保在显示之前已加载真实图像
     this.load();  
     this.realImage.display();
   }
}
// 客户端代码
const image1 = new ProxyImage("image1.jpg");
const image2 = new ProxyImage("image2.jpg");

// 图片尚未加载
image1.display();

// 图片已加载
image1.display();

// 已加载另一张图片
image2.display();

输出

这将导致以下结果 -

Loading image: image1.jpg
Displaying image: image1.jpg
Displaying image: image1.jpg
Loading image: image2.jpg
Displaying image: image2.jpg

JavaScript 中的行为设计模式

行为设计模式是软件开发设计模式的一个子集,专注于类和对象之间的交互和通信。它们关注类和对象如何协同工作并通信以履行职责。

责任链模式

责任链用于通过多个处理程序路由请求。每个处理程序决定是处理请求还是将其传递给链中的下一个处理程序。

我们可以使用与之前相同的示例来演示此模式,因为 Express 中的中间件就是处理程序,它可以执行请求,也可以将其转发给下一个处理程序。

迭代器模式

迭代器可用于遍历集合中的元素。这在当今的计算机语言中似乎并不重要,但过去却并非如此。

任何允许我们在数据结构之间进行迭代的内置 JavaScript 函数(for、forEach、for...of、for...in、map、reduce、filter 等等)都可以使用迭代器模式。

与任何遍历技术一样,我们编写代码来迭代具有高级结构(例如树或图)的数据。

观察者模式

观察者模式允许您创建一种订阅机制,该机制会将所观察项目的任何更改通知给多个项目。基本上,该规则就像在特定对象上设置一个事件监听器,当该对象执行我们期望的操作时,我们就会执行某些操作。

React 的 useEffect hook 就是一个很好的例子。 UseEffect 会在声明时执行提供的函数。

该钩子分为两部分:可执行函数和依赖项数组。如果数组为空(如下例所示),则每次显示组件时都会调用该函数。

useEffect(() => { console.log('组件已渲染') }, [])

如果我们在依赖项数组中声明任何变量,则该函数仅在这些变量发生变化时运行。

useEffect(() => { console.log('var1 已更改') }, [var1])

即使是简单的 JavaScript 事件监听器也可以被视为观察者。反应式编程和 RxJS 等工具也说明了这种方法,它们可以跨系统管理异步信息和事件。