#解释
#什么是单例模式?
单例就是保证一个类只有一个实例,实现的方法是判断实例是否存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。
在《JavaScript 设计模式与开发实践》中单例模式的定义是:
保证一个类仅有一个实例,并提供一个访问它的全局访问点
#模块作用
- 模块间通信
- 系统中某个类的对象只能存在一个
- 保护自己的属性和方法
#实现一个单例模式
下面例子中instance
就是实例变量,getInstance
方法是判断是否创建实例变量
class Singleton {
static getInstance(name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
constructor(name) {
this.name = name;
}
getName() {
console.log(this.name);
}
}
Singleton.instance = null;
const a = Singleton.getInstance("sven1");
const b = Singleton.getInstance("sven2");
console.log(a === b); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#使用闭包的方式实现
class Singleton {
constructor(name) {
this.name = name;
}
getName() {
console.log(this.name);
}
}
Singleton.getInstance = (() => {
let instance = null;
return name => {
if (!instance) {
instance = new Singleton(name);
}
return instance;
};
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#透明的单例模式
在上面的例子中,我们通过Singleton.getInstance()
方法来获取单例类,这个类并不直观(不透明性),因此我们使用透明,以下是创建一个 div 节点
const CreateDiv = (() => {
let instance = null;
const CreateDiv = function(html) {
if (instance) return instance;
this.html = html;
this.init();
return (instance = this);
};
CreateDiv.prototype.init = function() {
let div = document.createElement("div");
div.innerHTML = this.html;
document.body.appendChild(div);
};
return CreateDiv;
})();
const a = new CreateDiv("sven1");
const b = new CreateDiv("sven2");
console.log(a === b);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#用代理实现单例模式
在上面的例子中会感觉代码有些奇怪,同时代码生涩难懂,下面使用代理类来改善代码
class CreateDiv {
constructor(html) {
this.html = html;
this.init();
}
init() {
let div = document.createElement("div");
div.innerHTML = this.html;
document.body.appendChild(div);
}
}
const ProxySingletonCreateDiv = (() => {
let instance = null;
return function(html) {
if (!instance) {
instance = new CreateDiv(html);
}
return instance;
};
})();
const a = new ProxySingletonCreateDiv("sven1");
const b = new ProxySingletonCreateDiv("sven2");
console.log(a === b); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#JavaScript 中的“单例模式”
单例模式的核心是确保只有一个实例,并提供全局访问。全局变量不是单例模式,但在 JavaScript 开发中,我们经常会把全局变量当成单例来使用
以下其实是全局变量,但它满足了创建单例模式的条件
let home = {
style: "Chinese styles",
door() {
// ...
},
window() {
//...
}
};
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
#惰性单例
{% note primary %}
使用了惰性单例模式:在需要的时候创建单例,再次调用就使用第一次实例化后的实例对象
{% endnote %}
例如:点击登录按钮跳出弹窗就可以使用惰性单例模式
<button id="loginBtn">登录</button>
1
const createLoginLayer = () => {
let div = document.createElement("div");
div.innerHTML = "Modal";
div.style.display = "none";
document.body.appendChild(div);
return div;
};
const getSingle = fn => {
let result;
return function(...arg) {
return result || (result = fn(arg));
};
};
const createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById("loginBtn").addEventListener("click", () => {
let loginLayer = createSingleLoginLayer();
loginLayer.style.display = "block";
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#在 Vue 中的使用
#效果
点击按钮能够新增条目,点击条目能够删除这一行
#目录结构
|- src
| |- components
| |- dialog-container
| |- dialog-container.vue
| |- index.js
| |- App.vue
| |- main.js
1
2
3
4
5
6
7
2
3
4
5
6
7
#具体实现
<!-- dialog-container.vue -->
<template>
<div>
<ul>
<li v-for="list in lists" :key="list.id" @click="del(list.id)">
{{ list.content }}
</li>
</ul>
</div>
</template>
<script>
export default {
methods: {
del(id) {
const index = this.lists.findIndex(ele => ele.id === id);
if (-1 !== index) {
this.lists.splice(index, 1);
}
}
}
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
具体单例使用
import Vue from "vue";
import Container from "./dialog-container.vue";
Container.installSlot = (() => {
let [component, lists] = [null, null];
return (id, list) => {
if (typeof list === "object" && typeof id === "string") {
if (!component) {
const container = Vue.extend(Container);
lists = [list];
component = new container({
data() {
return {
lists
};
}
});
document.getElementById(id).appendChild(component.$mount().$el);
} else {
lists.push(list);
}
}
};
})();
export default {
install: Vue => {
Vue.prototype.$dialog = Container.installSlot;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
配置及使用
import Dialog from "./components/dialog-container";
Vue.use(Dialog);
1
2
2
<template>
<div id="app">
<div id="content">
<button @click="hi('content')">Hellos</button>
</div>
</div>
</template>
<script>
export default {
name: "App",
methods: {
hi(domId) {
const id = Math.random()
.toString()
.substr(2, 6);
// 使用
this.$dialog(domId, {
id,
content: `The id is ${id}`
});
}
}
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#总结
单例模式其他具体场景不是很清楚,希望以后能够看到其他用法,对于设计模式我也才刚开始看,继续努力。