JavaScript 设计模式学习笔记
设计模式简介
Good code is like a love letter to the next developer who will maintain it! 好的代码就像一封写给下一个维护者的情书!
模式是一种可重复使用的解决方案,可以将模式应用于软件设计中反复出现的问题和主题。
设计模式优势:
- 模式是行知有效的解决方案
- 是由开发人员的洞察力和经验构造的解题方法。
- 模式很容易重复使用
- 模式通常会提供一个开箱即用的解决方案。可根据自己的需求对其调整。
- 模式提供优雅的解决方案
如何构建模式
- 首先要确定模式的目标
- 概述模式的设计、实现、和目标
- 建立一下三个方面的关系
- context(上下文)
- 在上下文中建立的系统
- 系统允许的配置
模式具备的要素: - 模式名称:描述模式用途 - 模式描述:简要说明该模式有助于实现什么目标 - 情境概述:模式有效满足用户需求的情境 - 问题陈述:对问题的陈述,以便我们理解模式的意图 - 解决方案:以可理解的步骤和感知列表描述如何解决用户的问题。 - 设计:描述模式的设计,尤其是用户与模式交互时的行为。 - 实施: 开发人员如何实施该模式的指南。 - 图示: 模式中类的可视化表示(如图表)。 - 示例: 该模式的最小实现形式。 - 核心前提: 为支持所描述模式的使用,还需要哪些其他模式? - 关系: 这种模式与哪些模式相似?它与其他模式有相似之处吗? - 已知用法: 该模式是否被广泛使用?如果是,在哪里使用,如何使用? - 讨论: 团队或作者对该模式令人兴奋的优点的看法。
设计模式分类
- 创建型
- 处理对象的创建机制
- 构造器模式(Constructor)
- 工厂模式(Factory)
- 抽象模式(Abstract)
- 单例模式(Singleton)
- 原型模式(Prototype)
- 生成器模式(Builder)
- 结构型
- 处理对象之间的关系
- 代理模式(Proxy)
- 装饰器模式(Decorator)
- 门面模式(Facade)
- 适配器模式(Adapter)
- 行为型
- 改善或简化对象之间的通信
- 迭代器(Iterator)
- 中介者(Mediator)
- 观察者(Observer)
- 访问者(Visitor)
创建型
构造器模式(Constructor)
class Car {
constructor(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}
toString() {
return `${this.model} has done ${this.miles} miles`
}
}
let civic = new Car('Honda Civic', 2017, 20000)
class Car {
constructor(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}
}
Car.prototype.toString = function() {
return `${this.model} has done ${this.miles} miles`
}
模块化
通过 weakMap 可以实现模块化。
模块化可以帮助我们实现变量或方法的私有化。通过私有化,从而更好的对软件进行拆分,提高开发效率、降低理解成本、维护成本。
单例模式(Singleton)
场景: 将一个类的实例化限制为一个对象。通过此实例化对象,可以协调系统的操作,模块之间的协调。
缺点: - 不容易识别一个对象实例是不是单例 - 由于隐形依赖导致测试困难 - 因单例主要用于协调全局,所以执顺序相当重要
例子
let instance
function privateMethod() {}
class Singleton {
constructor() {
if(!instance) {
instance = this
}
return instance
}
publicMethod() {}
}
React 中的状态管理
使用 React 进行网页开发的开发人员可以通过 Redux 或 React Context 等状态管理工具来依赖全局状态,而不是 Singletons。与 Singletons 不同的是,这些工具提供的是只读状态,而不是可变状态。
虽然全局状态的缺点并不会因为使用这些工具而神奇地消失,但由于组件无法直接更新全局状态,我们至少可以确保全局状态按照我们的意图发生变化。
原型模式(Prototype)
原型模式指类似与原型继承的对象创建模式
可通过 Object.create 实现
const Car = {
name: 'F',
drive() {},
panic() {}
}
const mcar = Object.create(Car)
class VehiclePrototype { constructor(model) {
this.model = model; }
getModel() {
console.log('The model of this vehicle is..' +
this.model); }
Clone() {} }
class Vehicle extends VehiclePrototype { constructor(model) {
super(model); }
Clone() {
return new Vehicle(this.model); }
}
const car = new Vehicle('Ford Escort'); const car2 = car.Clone(); car2.getModel();
const beget = (() => { class F {
constructor() {}
}
return proto => { F.prototype = proto; return new F();
}; })();
工厂模式(Factory)
工厂模式为创建对象提供一个通用接口,通过此接口我们可以创建指定的对象。
class Car {
constructor({doors, state, color}) {
this.doors = doors
this.state = state
this.color = color
}
}
class Truck {
constructor({state, wheelSize, color}) {
this.state = state
this.wheelSize = wheelSize
this.color = color
}
}
class Factory {
constructor() {
this.vehicleClass = Car
}
create(options) {
switch (options.type) {
case 'car':
this.vehicleClass = Car
break
case 'truck':
this.vehicleClass = Truck
break
}
return new this.vehicleClass(options)
}
}
抽象工厂模式(Abstract Factories)
此模式可以理解为创建工厂的模式,通过此模式,可以创建服装工厂、汽车工厂、玻璃工厂、化工厂等。
class AbstractFactory {
constructor() {
this.types = {}
}
static getVehicle(type, customizations) {
const V = this.types[type]
return V ? new V(customizations) :null
}
static register(type, V) {
this.types[type] = Vehicle
return AbstractFactory
}
}
AbstractFactory.register('car', Car)
AbstractFactory.register('truck', Truck)
结构型
门面模式(Facade)
在 jquery 中经常看到,例如选择器的函数,$(‘div’).css()
const addEvent = (el, ev, fn) => {
if(el.addEventListener) {
// el.addEventListener
} else if(el.attachEvent) {
// el.attachEvent
} else {
// el[`on${en}`]
}
}
混入模式(Mixin)
通过 Mixin 可以扩展对象的功能。Mixin 有助于减少功能重复,提高复用率。
可以将 Mixin 视作对象子类化的一种替代方案。
React 推荐使用高阶组件和钩子来代替。
装饰器(Decorator Pattern)
可以将 Mixin 视作对象子类化的一种替代方案。
装饰器的目标是像现有类动态添加行为。
享元模式(Flyweight)
用于优化重复、缓慢和低效的共享数据。其目的是通过尽可能多地与相关对象共享数据,最大限度减少应用程序使用的内存。
在实践中,Flyweight 数据共享可能涉及多个对象使用的多个类似对象或数据结构,并将这些数据放入一个外部对象中。我们可以将该对象传递给依赖于这些数据的对象,而不是在每个对象中存储相同的数据。
可以将此模式应用于数据层或 DOM 层。
行为模型
观察者模式、发布订阅
中介者模式
命令模式
const CarManager = {
requestInfo(model, id) {
return `The information for ${model} with ID
${id} is foobar`;
},
buy(model, id) {
return `You have successfully purchased Item
${id}, a ${model}`;
},
viewing(model, id) {
return `You have booked a viewing of ${model} (
${id} ) `;
}
}
CarManager.execute = function (name) {
return (
CarManager[name] && CarManager[name].apply(CarManager, [].slice().call(arguments, 1))
)
}
CarManager.execute('buy', 'F', '001')
MV*
MVC
- Model
- 管理应用程序的数据,不涉及用户洁面,也不涉及表现层。当数据模型变化时,通常会通知观察者发生了变化,以便观察者做出相应的反应。
// MVC
// model 可以被 controll 更新,也可以被 view 更新
const model = {
count: 0,
increment: function() {
this.count++
}
}
const view = {
countEl: document.getElementById('count'),
incrementBtn: document.getElementById('increment'),
render: function () {
this.countEl.textContent = model.count
}
// 这里也可以调用 model 的方法
}
const controller = {
init: function () {
view.incrementBtn.addEventListener('click', this.increment)
},
increment: function() {
// 更改 model
model.increment()
// 更新 view
view.render()
}
}
// MVP
const model = {
count: 0,
increment: function() {
this.count++
}
// model 可以通知 Presenter
}
const view = {
countEl: document.getElementById('count'),
incrementBtn: document.getElementById('increment'),
render: function () {
this.countEl.textContent = model.count
}
}
const Presenter = {
init: function () {
view.incrementBtn.addEventListener('click', this.increment)
},
increment: function() {
// 更新 model
model.increment()
// 更新 view
view.render()
}
}
// MVVM
const model = {
count: 0,
increment: function() {
this.count++
}
}
const view = {
countEl: document.getElementById('count'),
incrementBtn: document.getElementById('increment'),
render: function () {
this.countEl.textContent = model.count
}
}
const Presenter = {
init: function () {
view.incrementBtn.addEventListener('click', this.increment)
},
increment: function() {
model.increment()
view.render()
}
}
模块化技术
- AMD
- 依赖可以异步加载
- Commonjs
- UMD
- 结合了 AMD 和 Commonjs
- ES Module