# 设计模式:单例模式

# 什么是单例模式

保证某个变量只被声明一次,并且提供全局的访问方法。

# 使用场景

比如浏览器中的 window 对象,整个网页只能有一个。

比如某些登录的悬浮窗口,无论你点击多少次登录按钮页面只会展示一个登录表单。在用户第二次点击登录按钮的时候一定会做判断是否已经展示了登录窗口,如果已经展示过了就不会再创建登录窗。这就是单例模式的常见应用。

# Coding

类中的单例模式

一个标准的单例模式并不复杂,无非使用变量来标记当前是否已经为某个类 new 过。

// 登录悬浮窗
var LoginWindow = function() {
  this.username = ''
  this.pwd = ''
  this.instance = null
}
// 静态单例方法,不会被继承
LoginWindow.getInstance = function() {
  this.instance = this.instance || new LoginWindow()
  return this.instance
}

let l_1 = LoginWindow.getInstance();
let l_2 = LoginWindow.getInstance();
console.log(l_1 === l_2); // true

ES6 写法

class LoginWindow{
  static getInstance() {
    this.instance = this.instance || new LoginWindow();
    return this.instance;
  }
  constructor() {
    this.username = '';
    this.pwd = '';
    this.instance = null;
  }
}

let l_1 = LoginWindow.getInstance();
let l_2 = LoginWindow.getInstance();
console.log(l_1 === l_2); // true

用代理实现单例模式

某些情况下,我们并不想为某个类增加单例函数。此时我们可以借用闭包来实现一个通用的单例函数。

// 普通类
const LoginWindow = function() {
  this.username = '';
  this.pwd = '';
  this.instance = null;
}

// 通用的单例函数
const Singleton = function(fn) {
  let instance = null
  return function() {
    return instance || (instance = fn.apply(this, [].slice.call(arguments)));
  }
}

// 单例类
const SingletonLoginWindow = Singleton(LoginWindow);

let l_1 = SingletonLoginWindow();
let l_2 = SingletonLoginWindow();
console.log(l_1 === l_2) // true

透明的单例模式

所谓透明,是指两次 new 出来的结果都指向一个内存地址。

let LoginWindow =  function() {
  this.username = ''
  this.pwd = ''
}
LoginWindow.prototype.login = function() {}

const ProxySingletonLoginWindow = (function() {
  let instance;
  return function() {
    return instance || (instance = new LoginWindow())
  }
})();

let l_1 = new ProxySingletonLoginWindow();
let l_2 = new ProxySingletonLoginWindow();
console.log(l_1 === l_2);

这样做的好处是,单利 的实现分开。并且构建对象依然用了 new 关键字。

# 总结

单例模式在软件工程中非常常见,尤其是惰性单利。本章还提到了代理模式,后面的章节会详细介绍代理模式。

我们在用代理模式实现单例的时候会用到闭包和高阶函数的概念,这两者结合可以轻易的实现惰性单例。

上次更新: 9/9/2020, 7:36:46 PM