单例对象的类只能允许一个实例存在。
思路
- 有一个引用类对象
- 这个对象实例永远只有一个
- 实现的基本步骤:
- 将构造函数定义为私有函数,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例
- 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
单例模式的优点
系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
缺点
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法
适用场合
- 需要平凡进行创建和销毁的对象
- 创建对象时耗时过多或者耗费资源过多,但又经常用到的资源
- 工具类对象
- 频繁访问数据库或者文件对象(缓存
实现单例模式
1.客户端, 就是使用这类的使用者必须知道是一个单例的类,必须主动条用getInstance方法
2.并不能真正的阻止客户端直接new Window
- es6 实现
class Window {
constructor (name) {
this.name = name;
}
static getInstance () {
if (!this.instance) {
this.instance = new Window()
}
return this.instance
}
}
let w1 = Window.getInstance()
let w2 = Window.getInstance()
console.log(w1 === w2)
- es5 实现
function Window (name) {
this.name = name
}
// 类上的方法,只能通过类来访问
Window.getInstance = (function () {
let instance
return function () {
if (!instance) {
instance = new Window
}
return instance
}
})()
let w1 = Window.getInstance()
let w2 = Window.getInstance()
console.log(w1 === w2)
透明单利
let window = (function() {
let window;
let Window = function (name) {
if (window) {
return window
} else {
this.name = name
return (window = this)
}
}
return Window
})();
/*
* 1. 创建this = 空对象
* 2. new 关键字, 如果返回的是一个对象,那new 出来的对象就是我们返回的对象
* 3. 虽然这么写能够实现单例,但是违反了函数单一原则的设计理念,需要进一步优化
*/
let w1 = Window.getInstance()
let w2 = Window.getInstance()
- 优化书写方法
思路: 把类的实例的创建逻辑和单例逻辑分开
function Window (name) {
this.name = name
}
Window.prototype.getName = function () {
console.log(this.name)
}
let CreateSingle = (function () {
let instance;
return function (name) {
if (!instance) {
instance = new Window(name)
}
return instance
}
})()
let w1 = new CreateSingle('hello')
let w2 = new CreateSingle('hello')
console.log(w1 === w2)
- 进一步优化
上边的写法不能创建多个实例,这个进一步优化
function Window (name) {
this.name = name
}
function Dialog (name) {
this.name = name
}
Window.prototype.getName = function () {
console.log(this.name)
}
let CreateSingle = function (Constructor) {
let instance;
return function () {
if (!instance) {
Constructor.apply(this, arguments)
// 下边 两行代码意思相同
// this.__proto = ConStructor.prototype
Object.setPrototypeOf(this, Constructor.prototype)
instance = this;
}
return instance
}
}
let CreateWindow = CreateSingle(Window)
let w1 = new CreateWindow('hello')
let w2 = new CreateWindow('leo')
console.log(w1 === w2)
let CreateDialog = CreateSingle(Dialog)
let d1 = new CreateDialog('hello')
let d2 = new CreateDialog('leo')
console.log(d1 === d2)
- 优化CreateSingle方法
function Window (name) {
this.name = name
}
function Dialog (name) {
this.name = name
}
Window.prototype.getName = function () {
console.log(this.name)
}
/*let CreateSingle = function (Constructor) {
let instance;
let SingleConstructor = function () {
if (!instance) {
Constructor.apply(this, arguments)
instance = this;
}
return instance
}
// 原形继承
SingleConstructor.prototype = Object.create(Constructor.prototype)
return SingleConstructor
}*/
let CreateSingle = function (Constructor) {
let instance;
let SingleConstructor = function () {
if (!instance) {
instance = new Constructor(...arguments)
}
return instance
}
// 原形继承
return SingleConstructor
}
let CreateWindow = CreateSingle(Window)
let w1 = new CreateWindow('hello')
let w2 = new CreateWindow('leo')
console.log(w1 === w2)
let CreateDialog = CreateSingle(Dialog)
let d1 = new CreateDialog('hello')
let d2 = new CreateDialog('leo')
console.log(d1 === d2)
单利模式应用
- 命名空间
- jQuery
- 复杂对象想的可读性
// 示例
let utils = {}
utils.define = function (namespace, fn) {
namespace = namespace.split('.')
let fnName = namespace.pop()
let curr = utils
for (let i = 0; i < namespace.length; i++) {
let name = namespace[i]
if (!curr[name]) {
curr[name] = {} // 作为容器
}
curr = curr[name]
}
curr[fnName] = fn
}
utils.define('req', function () {console.log('req')})
utils.define('requst.header', function () {console.log('utils.req.header')})
console.log(utils.req())
console.log(utils.requst.header())
适用场景
- redux, 数据共享
- 常用组件
<!DOCTYPE html>
<html>
<head>
<title>登录框</title>
<style type="text/css">
.login{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 320px;
height: 200px;
background: #eee;
border: 1px solid #e4e4e4;
border-radius: 5px;
display: none;
text-align: center;
}
.show{
display: block;
}
</style>
</head>
<body>
<p><button class="show">显示登录框</button><button class="hide">隐藏登陆框</button></p>
</body>
<script type="text/javascript">
let show = document.querySelector('.show')
let hide = document.querySelector('.hide')
class Login {
constructor () {
this.el = document.createElement('div')
this.el.classList.add('login')
this.el.innerHTML = (`
<p><input type="input" name="name" placeholder="账户" /></p>
<p><input type="password" name="password" placeholder="密码"></p>
<p><button>登录</button></p>
`)
document.body.appendChild(this.el)
}
show () {
this.el.classList.add('show')
}
hide () {
this.el.classList.remove('show')
}
static getInstance () {
if (!this.instance) {
this.instance = new Login()
}
return this.instance
}
}
show.addEventListener('click', function () {
Login.getInstance().show()
})
hide.addEventListener('click', function () {
Login.getInstance().hide()
})
</script>
</html>
总结
始终记得一句话,凡是要知其然,知其所以然…