DAILY DOCDAILY DOC
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
  • BFC 块级格式化上下文
  • Note
  • WebAssembly
  • public api
  • 位运算
  • bitwise operator
  • css实现隐藏效果
  • css snippets
  • 抖音点赞
  • js 相等判断
  • fetch ReadableStream
  • git
  • Github Actions 工作流
  • google search
  • RPC vs HTTP
  • gravatar
  • hhkb
  • Init project
  • input 文件上传
  • mac

    • Mac 使用技巧
    • alfred
    • mac shortcuts
    • shortcuts text edit
    • mac 修改host
  • 微前端
  • mock
  • nginx dump
  • nginx
  • NirCmd
  • npm
  • Operator Precedence
  • package.json
  • url query 解析
  • pnpm
  • JavaScript Precise countdown
  • react 模版
  • regexp
  • setup web development
  • telegram

    • telegram bot
  • timeFunction ease
  • 视频裁剪
  • vscode

    • vscode 高级指南
    • bracketPairs
    • jsconfig.json
    • vscode pipe into code
    • social project
    • vscode tasks
  • draggable resizable
  • windows 激活
  • 前端截图实现
  • 文本配音 富文本实现
  • 图片处理
  • 前端坐标
  • 定时任务
  • work efficient
  • 微信小程序动画实现方案
  • 排列组合
  • 数列
  • 语音驱动文字
  • 浏览器
  • 状态管理
  • 移动盒子
  • 移动端开发常用snippets
  • 设计模式
  • web performance

设计模式

创建型设计模式

  • Constructor 构造器
  • Factory 工厂
  • Abstract 抽象
  • Prototype 原型
  • Singleton 单例
  • Builder 生成器
  • 结构型设计模式
  • Decorator 装饰者
  • Facade 外观
  • Flyweight 享元
  • Adapter 适配器
  • Proxy 代理

行为设计模式

  • Iterator 迭代器
  • Mediator 中介者
  • Observer 观察者
  • Visitor 访问者

Revealing Module 揭示模块

IIFE 实现私有化模块

Javascript
const revealingModule = (function () {
  let privateVar = 'xxx';
  function privateFn() {
    console.log(privateVar);
  }
  function publicSet(val) {
    privateVar = val;
  }
  function publicGet() {
    return privateFn();
  }

  return {
    getVal: publicGet,
    setVal: publicSet,
  };
})();

单例模式

Javascript
const Singleton = (function () {
  let instance;

  function init() {
    // 私有属性和方法
    const prop1 = 'value1';
    const prop2 = 'value2';
    function method1() {
      console.log('method1');
    }
    function method2() {
      console.log('method2');
    }

    // 公有属性和方法
    return {
      prop1,
      prop2,
      method1,
      method2,
    };
  }

  return {
    // 获取单例实例的方法
    getInstance() {
      if (!instance) {
        instance = init();
      }
      return instance;
    },
  };
})();
details

单例模式是一种设计模式,它限制一个类在应用程序中只能有一个实例,并提供全局访问点。单例模式在 JavaScript 中可以通过多种方式实现,以下是几种常见的实现方法:

1. 使用闭包和立即执行函数表达式(IIFE)

这是一个最常见的方式,通过闭包和立即执行函数来实现单例模式:

const Singleton = (function() {
    let instance;

    function createInstance() {
        const object = new Object("I am the instance");
        return object;
    }

    return {
        getInstance: function() {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

2. 使用类

如果你使用 ES6 及更高版本,你可以通过类来实现单例模式:

class Singleton {
    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }
        Singleton.instance = this;
        // 其他初始化代码
    }
}

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2); // true

3. 使用模块

在 Node.js 或者其他支持模块的环境中,可以利用模块的特性来实现单例模式。模块在第一次被 require 或 import 时会被缓存,因此可以确保单例实例。

Node.js 环境

// Singleton.js
class Singleton {
    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
            // 其他初始化代码
        }
        return Singleton.instance;
    }
}

module.exports = Singleton;

// main.js
const Singleton = require('./Singleton');

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2); // true

ES6 模块环境

// Singleton.js
class Singleton {
    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
            // 其他初始化代码
        }
        return Singleton.instance;
    }
}

export default Singleton;

// main.js
import Singleton from './Singleton.js';

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2); // true

4. 使用 Symbol 和 Object.freeze

这种方法可以防止修改单例的实例:

const Singleton = (function() {
    const INSTANCE = Symbol();

    class Singleton {
        constructor(token) {
            if (token !== INSTANCE) {
                throw new Error('Cannot instantiate directly.');
            }
            // 初始化代码
        }

        static getInstance() {
            if (!this[INSTANCE]) {
                this[INSTANCE] = new Singleton(INSTANCE);
            }
            return this[INSTANCE];
        }
    }

    Object.freeze(Singleton);
    return Singleton;
})();

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

工厂模式

工厂模式指的是定义一个用于创建对象的接口,让子类决定实例化哪一个类。在 JavaScript 中可以使用构造函数实现工厂模式

Javascript
function Factory(type) {
  if (type === 'product1') {
    return new Product1();
  } else if (type === 'product2') {
    return new Product2();
  }
}

function Product1() {
  this.name = 'Product1';
}

function Product2() {
  this.name = 'Product2';
}

观察者模式

观察者模式指的是定义对象间一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都会收到通知并自动更新。在 JavaScript 中可以使用发布订阅模式实现观察者模式。

Javascript
class Subject {
  constructor() {
    this.observers = [];
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }

  notifyObservers() {
    for (const observer of this.observers) {
      observer.update();
    }
  }
}

class Observer {
  constructor() {}

  update() {
    console.log('I am updated');
  }
}

装饰器模式

装饰器模式指的是在不改变原有对象的基础上,通过对其进行包装或者添加新的功能来扩展其功能。在 JavaScript 中可以使用装饰器实现装饰器模式。

Javascript
function decorate(originalFunc) {
  return function () {
    console.log('Before originalFunc');
    const result = originalFunc.apply(this, arguments);
    console.log('After originalFunc');
    return result;
  };
}

function originalFunc() {
  console.log('Inside originalFunc');
}

const decoratedFunc = decorate(originalFunc);
decoratedFunc();

发布-订阅模式

Javascript
class EventEmitter {
  constructor() {
    this.events = {};
  }

  // 订阅事件
  on(eventName, listener) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(listener);
  }

  // 取消订阅
  off(eventName, listener) {
    if (!this.events[eventName]) {
      return;
    }
    const index = this.events[eventName].indexOf(listener);
    if (index !== -1) {
      this.events[eventName].splice(index, 1);
    }
  }

  // 触发事件
  emit(eventName, ...args) {
    if (!this.events[eventName]) {
      return;
    }
    // ⚠️⚠️⚠️ 该实现方法有问题,会导致 once 限执行的时候 on 的事件不会触发
    // this.events[eventName].forEach(listener => {
    //   listener.apply(this, args);
    // });

    //  浅拷贝一份 也可以。禁止遍历 数组的时候 同时修改数组
    this.events[eventName].slice().forEach(listener => {
      listener.apply(this, args);
    });

    // 倒序执行 避免 删除数组中 元素后导致 遍历不到
    // const len = this.events[eventName].length;
    // for (let i = len - 1; i >= 0; i--) {
    //   this.events[eventName][i].apply(this, args);
    // }
  }

  // 订阅一次性事件
  once(eventName, listener) {
    const wrapper = (...args) => {
      this.off(eventName, wrapper);
      listener.apply(this, args);
    };
    this.on(eventName, wrapper);
  }
}

const emitter = new EventEmitter();
emitter.once('hi', arg => {
  console.log('once response to', arg);
});
emitter.on('hi', arg => {
  console.log('response to ', arg);
});

emitter.emit('hi', 'micheal');
emitter.emit('hi', 'jodan');

特别需要注意 emit 的时候 once 方法会修改原数组,需要拷贝一份,或者 用 index 倒序遍历,不然会出现事件不会触发的情况

Javascript

nodejs 源码中 lib/events.js nodejs 中实现是通过拷贝的方式存储了一份,所以能保证正确结果;

EventEmitter.prototype.emit = function emit(type, ...args) {
  const handler = events[type];

  const len = handler.length;
  const listeners = arrayClone(handler);

  for (let i = 0; i < len; ++i) {
    const result = listeners[i].apply(this, args);
  }
};

function arrayClone(arr) {
  // At least since V8 8.3, this implementation is faster than the previous
  // which always used a simple for-loop
  switch (arr.length) {
    case 2:
      return [arr[0], arr[1]];
    case 3:
      return [arr[0], arr[1], arr[2]];
    case 4:
      return [arr[0], arr[1], arr[2], arr[3]];
    case 5:
      return [arr[0], arr[1], arr[2], arr[3], arr[4]];
    case 6:
      return [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]];
  }
  return ArrayPrototypeSlice(arr); // 改代码应该是c++ 实现的注入到js
}
Last Updated:
Contributors: rosendo
Prev
移动端开发常用snippets
Next
web performance