【夯实基础】《JavaScript设计模式与开发实践》笔记——单例模式

单例模式

单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的 window 对象等。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我 们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少 次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

实现单例模式

// 用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象
var Singleton = function( name ){
	this.name = name;
};
Singleton.prototype.getName = function(){
	alert ( this.name );
};
Singleton.getInstance = (function(){
	var instance = null;
	return function( name ){
		if ( !instance ){
			instance = new Singleton( name );
		}
		return instance;
	}
})();
  • 缺点:
    增加了这个类的“不透明性”,Singleton 类的使用者必须知道这是一个单例类, 跟以往通过 new XXX 的方式来获取对象不同,这里偏要使用 Singleton.getInstance 来获取对象。

透明的单例模式

目标是实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样。例如使用 CreateDiv 单例类,它的作用是负责在页面中创建唯一的 div 节点

var CreateDiv = (function(){
	var instance;
	var CreateDiv = function( html ){
		if ( instance ){
			return instance;
		}
		this.html = html;
		this.init();
		return instance = this;
	};
	CreateDiv.prototype.init = function(){
		var div = document.createElement( 'div' ); div.innerHTML = this.html;
		document.body.appendChild( div );
	};
	return CreateDiv;
})();
  • 缺点
    1. 为了把 instance 封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回
      真正的 Singleton 构造方法,这增加了一些程序的复杂度,阅读起来也不是很舒服。

用代理实现单例模式

我们依然使用之前的代码,首先在 CreateDiv 构造函数中,把负责管理单例的代码移除 出去,使它成为一个普通的创建 div 的类

var CreateDiv = function( html ){
	this.html = html;
	this.init();
};
CreateDiv.prototype.init = function(){
	var div = document.createElement( 'div' );
	div.innerHTML = this.html;
	document.body.appendChild( div );
};

引入代理类 proxySingletonCreateDiv

var ProxySingletonCreateDiv = (function(){
	var instance;
	return function( html ){
		if ( !instance ){
			instance = new CreateDiv( html );
		}
		return instance;
	}
})();
var a = new ProxySingletonCreateDiv( 'sven1' );
var b = new ProxySingletonCreateDiv( 'sven2' );
alert( a === b );

JavaScript 中的单例模式

JavaScript 是一门无类(class-free)语言,生搬单例模式的概念并无意义。
单例模式的核心是确保只有一个实例,并提供全局访问。

全局变量不是单例模式,但在 JavaScript 开发中,我们经常会把全局变量当成单例来使用,例如:

var a = {};

当用这种方式创建对象 a 时,对象 a 确实是独一无二的。如果 a 变量被声明在全局作用域下, 则我们可以在代码中的任何位置使用这个变量,全局变量提供给全局访问是理所当然的。这样就 满足了单例模式的两个条件。
缺点: 容易造成命名空间污染

相对降低全局变量带来命名污染的方式:

  • 使用命名空间
    适当地使用命名空间,并不会杜绝全局变量,但可以减少全局变量的数量。例如:
var namespace1 = {
	a: function(){
		alert (1);
    },
    b: function(){
		alert (2);
	}
};

或者(引自 Object-Oriented JavaScrtipt 一书)动态地创建命名空间

var MyApp = {};
MyApp.namespace = function( name ){ 
	var parts = name.split( '.' );
	var current = MyApp;
	for ( var i in parts ){
		if ( !current[ parts[ i ] ] ){
			current[ parts[ i ] ] = {};
		}
		current = current[ parts[ i ] ];
  	}
};
MyApp.namespace( 'event' );
MyApp.namespace( 'dom.style' );
console.dir( MyApp ); // 上述代码等价于:
var MyApp = {
	event: {},
	dom: {
		style: {}
	}
};
  • 使用闭包封装私有变量
    这种方法把一些变量封装在闭包的内部,只暴露一些接口跟外界通信:

var user = (function(){
	var __name = 'sven',
		__age = 29;
	return {
		getUserInfo: function(){
			return __name + '-' + __age; }
		}
	}
})();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值