# 单例模式 (singleton pattern)

# 什么是单例模式

产生一个类的唯一实例。
确保只有一个实例,并提供全局访问。

# 好处

  • 1.确保所有的对象都访问同一个实例
  • 2.更好的控制唯一实例
  • 3.降低全局变量带来的命名污染
  • 4.减少创建对象的开销
  • ...

# 缺点

  • 1.没有抽象层,拓展较为困难
  • 2.职责过重,违背了“单一原则”
  • ...

# 代码实现

静态属性实现

function Preson() {
    if (Preson.instance) {        // 判断是否已经有单例了
        return Preson.instance;
    }
    Preson.instance = this;
    return this;
}
var personA = new Preson();
var personB = new Preson();
console.log(personA === personB); // true
1
2
3
4
5
6
7
8
9
10

闭包实现

// 创建单例
let createSingleton = function(func) {
    let instance;
    return function() {
        return instance || (instance = func.apply(this,arguments));
    }
};

function Person(name) {
    this.name = name;
    return this;
}

let createPerson = createSingleton(function(name){
    return new Person(name);
})


let personA = createPerson('张三');
console.log(personA); // Person {name: "张三"}
let personB = createPerson('李四');
console.log(personB); // Person {name: "张三"}
console.log(personA === personB); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

ES6实现

class Person {
    constructor(name) {
        this.name = name;
        this.instance = null;
    }

    getInstance(name) {
        if(!this.instance) {
            return this.instance = new Person(name);
        }
        return this.instance;
    }
}
let person = new Person();
console.log(person.getInstance('张三'));
console.log(person.getInstance('李四'));
// Person {name: "张三", instance: null}
// Person {name: "张三", instance: null}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 实际场景

场景一:登录弹窗
需求: 多次点击只能弹一次窗口,并且关闭后再次打开不重新创建dom节点

相关代码

<template>
  <div>
      <button @click="openPopup">
          <slot></slot>
      </button>
  </div>
</template>

<script>
// 单例模式
let createSingleton = function(func) {
    let instance;
    return function() {
        return instance || (instance = func.apply(this,arguments));
    }
};
let createPopup = createSingleton(() => {
    let div = document.createElement('div');
    div.className = 'popup-shade-box';
    div.innerHTML = '登录弹窗';
    let closeNode = document.createElement('i');
    closeNode.className = 'popup-close';
    closeNode.innerHTML = 'X';
    div.appendChild(closeNode);
    document.body.appendChild(div);
    closeNode.addEventListener('click', () => {
        div.style.display = 'none';
    })
    return div;
});

export default {
    methods: {
        openPopup() {
           createPopup().style.display='block';
        }
    }
}
</script>

<style>
@keyframes move-in {
    0% {top: 150px}
    50% {top: 120px}
}
@keyframes move-out {
    0% {top: 150px}
    50% {top: 120px}
}
.popup-shade-box {
    z-index: 999;
    position: fixed;
    left:0;
    right:0;
    top: 120px;
    width: 500px;
    height: 300px;
    border-radius: 5px;
    margin: 0 auto;
    text-align: center;
    line-height: 30px;
    color: #fff;
    background: rgba(0,0,0, 0.8);
    animation: move-in .5s ease;
}
.popup-close{
    position: absolute;
    right: 5px;
    border: 5px solid transparent;
    cursor: pointer;
}
</style>
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72