本博客 hjy-xh,转载请申明出处
定义
单例模式,也叫单子模式,是一种常用的软件设计模式,属于创建型模式
的一种。
保证一个类仅有一个实例
,并提供一个访问它的全局访问点
。
思考
全局变量符合单例模式吗?
不是。但我们经常会把变量当成单例来使用,看个例子:
1 | var person = {}; |
通过字面量创建对象时,对象person
确实是独一无二的,如果该变量在全局作用域下声明,就可以在代码中的任何地方使用它。
但是全局变量存在一些问题:
- 污染命名空间(变量名冲突)
- 不易维护 (被覆盖)
并且随着项目的体积和功能增大,出现问题的概率也会增大。
实现
首先我们要清楚 JS 是一门没有类
的语言,ES6 出现类也是原型的语法糖。也正因为没有类,在 JS 中实现单例模式也只需要一个唯一
的对象,这是很自然的做法。
这里以一个登录弹窗为例,实践一下单例模式。
假设现在有一个登录按钮,点击后能够出现登录弹窗:
1 | <button id="loginBtn">登录</button> |
先来写创建登录弹窗的方法:
1 | var doCreateLoginModal = function () { |
接下来就是单例模式的重点了:
1 | var getInstance = function (fn) { |
这里可以发现返回的结果被封装在闭包(内部的函数被保存到了外部)产生的作用域中,外部是访问不到这两个变量的,这就避免了对全局的命名污染。
先看这段代码中产生的闭包:
最后一行外部的createLoginModal
变量保存了getInstance
中的匿名函数,该拥有getInstance
作用域的访问权限。
再仔细看return result || (result = fn.apply(this, arguments));
这条语句:
第一次调用方法时,result
为undefined
,会执行result = fn.apply(this, arguments)
,这里利用传入的fn
调用生成登录弹窗的方法生成登录弹窗,并被赋值给result
,使得之后createLoginModal
再被调用时,返回第一次创建的登录弹窗。
最后给按钮绑定点击事件:
1 | var loginBtn = document.getElementById("loginBtn"); |
惰性单例
惰性单例指的是在需要的时候菜创建对象实例。
参考
书目
JavaScript 设计模式与开发实践
JavaScript 设计模式