js设计模式【详解】—— 单例模式

简介: js设计模式【详解】—— 单例模式

单例模式的定义

单例模式:一个类只能有一个实例,即使多次实例化该类,也只返回第一次实例化后的实例对象。

核心要点:确保只有一个实例, 并提供全局访问。

推荐使用场景:用单例模式进行命名空间,管理模块

优点:

1. 减少不必要的内存开销

2. 减少全局的函数和变量冲突

演示范例——通过对象字面量创建对象的方式实现单例模式

得益于JavaScript创建对象的方式十分灵活, 可以直接通过对象字面量的方式实例化一个对象, 而其他面向对象的语言必须使用类进行实例化,所以下方代码中定义的对象timeTool就已经是一个单例模式创建对象的实例(letconst不允许重复声明的特性,确保了timeTool不能被重新覆盖)

let timeTool = {
  name: '处理时间工具库',
  getISODate: function() {},
  getUTCDate: function() {}
}

演示范例——通过定义类实现单例模式

class SingletonApple {
  constructor(name, creator, products) {
      this.name = name;
      this.creator = creator;
      this.products = products;
  }
  //静态方法
  static getInstance(name, creator, products) {
    if(!this.instance) {
      this.instance = new SingletonApple(name, creator, products);
    }
    return this.instance;
  }
}
 
let appleCompany = SingletonApple.getInstance('苹果公司', '乔布斯', ['iPhone', 'iMac', 'iPad', 'iPod']);
let copyApple = SingletonApple.getInstance('苹果公司', '阿辉', ['iPhone', 'iMac', 'iPad', 'iPod'])
 
console.log(appleCompany === copyApple); //true

通过类的静态方法getInstance创建实例,若是第一次创建对象实例则new一个对象实例(触发构造函数constructor),若检测到已进行过实例化,则返回已创建过的对象实例,不再重新创建实例。

应用场景——命名空间

命名空间:全局只暴露一个对象名,将变量作为该对象的属性,将方法作为该对象的方法,这样就能大大减少全局变量的个数,用来解决全局变量冲突的问题

//开发者A写了一大段js代码
let devA = {
  addNumber() { }
}
 
//开发者B开始写js代码
let devB = {
  add: ''
}
 
//A重新维护该js代码
devA.addNumber();

devAdevB就是两个命名空间,采用命名空间可以有效减少全局变量的数量,以此解决变量冲突的发生。

应用场景——管理模块

var devA = (function(){
  //ajax模块
  var ajax = {
    get: function(api, obj) {console.log('ajax get调用')},
    post: function(api, obj) {}
  }
 
  //dom模块
  var dom = {
    get: function() {},
    create: function() {}
  }
  
  //event模块
  var event = {
    add: function() {},
    remove: function() {}
  }
 
  return {
    ajax: ajax,
    dom: dom,
    event: event
  }
})()

上面的代码库中有ajax,domevent三个模块,用同一个命名空间devA来管理。在进行相应操作的时候,只需要devA.ajax.get()进行调用即可,这样可以让库的功能更加清晰。

项目实战范例——单例模式

范例功能:点击登录按钮后永远只返回一个登录框的实例。

实现思路:

  1. 给顶部导航模块的登录按钮注册点击事件
  2. 登录按钮点击后JS动态创建遮罩层和登陆弹框
  3. 遮罩层和登陆弹框插入到页面中
  4. 给登陆框中的关闭按钮注册事件, 用于关闭遮罩层和弹框

……

代码解析:

第一次点击登录按钮时,调用Login.getInstance()实例化了一个登录框,在之后的点击中,不再重新创建新的登录框,只是移除掉"display: none"这个样式来显示登录框,节省了内存开销。

1. 给页面添加顶部导航栏的HTML代码

  <nav class="top-bar">
    <div class="top-bar_left">
      LTH BLOG
    </div>
    <div class="top-bar_right">
      <div class="login-btn">登录</div>
      <div class="signin-btn">注册</div>
    </div>
  </nav>

2. 使用ES6的语法创建Login类

class Login {
 
  //构造器
  constructor() {
    this.init();
  }
 
  //初始化方法
  init() {
    //新建div
    let mask = document.createElement('div');
    //添加样式
    mask.classList.add('mask-layer');
    //添加模板字符串
    mask.innerHTML = 
    `
    <div class="login-wrapper">
      <div class="login-title">
        <div class="title-text">登录框</div>
        <div class="close-btn">×</div>
      </div>
      <div class="username-input user-input">
        <span class="login-text">用户名:</span>
        <input type="text">
      </div>
      <div class="pwd-input user-input">
        <span class="login-text">密码:</span>
        <input type="password">
      </div>
      <div class="btn-wrapper">
        <button class="confrim-btn">确定</button>
        <button class="clear-btn">清空</button>
      </div>
    </div>
    `;
    //插入元素
    document.body.insertBefore(mask, document.body.childNodes[0]);
 
    //注册关闭登录框事件
    Login.addCloseLoginEvent();
  }
 
  //静态方法: 获取元素
  static getLoginDom(cls) {
    return  document.querySelector(cls);
  }
 
  //静态方法: 注册关闭登录框事件
  static addCloseLoginEvent() {
    this.getLoginDom('.close-btn').addEventListener('click', () => {
      //给遮罩层添加style, 用于隐藏遮罩层
      this.getLoginDom('.mask-layer').style = "display: none";
    })
  }
 
  //静态方法: 获取实例(单例)
  static getInstance() {
    if(!this.instance) {
      this.instance = new Login();
    } else {
      //移除遮罩层style, 用于显示遮罩层
      this.getLoginDom('.mask-layer').removeAttribute('style');
    }
    return this.instance;
  }
}

3. 给登录按钮添加注册点击事件

//注册点击事件
Login.getLoginDom('.login-btn').addEventListener('click', () => {
  Login.getInstance();
})

更多设计模式详见——js设计模式【详解】总目录

https://blog.csdn.net/weixin_41192489/article/details/116154815

目录
相关文章
|
1天前
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 职责链模式
js设计模式【详解】—— 职责链模式
21 8
|
1天前
|
设计模式 JavaScript Go
js设计模式【详解】—— 状态模式
js设计模式【详解】—— 状态模式
17 7
|
1天前
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 组合模式
js设计模式【详解】—— 组合模式
20 7
|
1天前
|
设计模式 JavaScript
js设计模式【详解】—— 桥接模式
js设计模式【详解】—— 桥接模式
15 6
|
1天前
|
设计模式 JavaScript
js设计模式【详解】—— 原型模式
js设计模式【详解】—— 原型模式
14 6
|
1天前
|
设计模式 JavaScript 算法
js设计模式【详解】—— 模板方法模式
js设计模式【详解】—— 模板方法模式
17 6
|
1天前
|
设计模式 存储 JavaScript
js设计模式【详解】—— 享元模式
js设计模式【详解】—— 享元模式
15 6
|
2月前
|
设计模式 JavaScript 算法
js设计模式-策略模式与代理模式的应用
策略模式和代理模式是JavaScript常用设计模式。策略模式通过封装一系列算法,使它们可互换,让算法独立于客户端,提供灵活的选择。例如,定义不同计算策略并用Context类执行。代理模式则为对象提供代理以控制访问,常用于延迟加载或权限控制。如创建RealSubject和Proxy类,Proxy在调用RealSubject方法前可执行额外操作。这两种模式在复杂业务逻辑中发挥重要作用,根据需求选择合适模式解决问题。
|
12月前
|
设计模式 算法 JavaScript
|
设计模式 算法 JavaScript
你不知道的javascript设计模式(六)---- 策略模式
你不知道的javascript设计模式(六)---- 策略模式
114 0