单例模式: 如点击登陆时弹出的登陆页面只有一个,并不会因为你多点几下登陆就出现多个登陆框。
1. 代理实现单例模式(传统面向对象)
把负责管理单例的逻辑移到了代理类proxySingletonCreateDiv中,这样,CreateDiv就变成了一个普通的类,它跟proxySingletonCreateDiv组合起来可以达到单例模式的效果。
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 );
2. JavaScript中的单例模式
核心是确保只有一个实例,并提供全局访问。全局变量不是单例模式,但在JavaScript 开发中,我们经常会把全局变量当成单例来使用。
全局变量主要是注意命名污染的问题,可通过使用命名空间以及使用闭包封装私有变量的方法来降低。
3. 惰性单例
即在需要的时候才创建对象实例。(以QQ登陆悬浮框为例),为避免浪费DOM节点,使用户点击登陆按钮的时候才开始创建该浮窗, 同时避免频繁的删除和重建节点, 使用一个变量来判断是否已经创建过登陆浮窗:
var createLoginLayer = (function(){ var div; return function(){ if ( !div ){ div = document.createElement( 'div' ); div.innerHTML = '我是登录浮窗'; div.style.display = 'none'; document.body.appendChild( div ); } return div; } })(); document.getElementById( 'loginBtn' ).onclick = function(){ var loginLayer = createLoginLayer(); loginLayer.style.display = 'block'; };
4. 通用的惰性单例
现在我们就把如何管理单例的逻辑从原来的代码中抽离出来,这些逻辑被封装在getSingle
函数内部,创建对象的方法fn 被当成参数动态传入getSingle 函数:
var getSingle = function( fn ){ var result; return function(){ return result || ( result = fn .apply(this, arguments ) ); } };
接下来将用于创建登录浮窗的方法用参数fn 的形式传入getSingle,我们不仅可以传入createLoginLayer,还能传入createScript、createIframe、createXhr 等。之后再让getSingle 返回
一个新的函数,并且用一个变量result 来保存fn 的计算结果。result 变量因为身在闭包中,它永远不会被销毁。在将来的请求中,如果result 已经被赋值,那么它将返回这个值。代码如下:
var createLoginLayer = function(){ var div = document.createElement( 'div' ); div.innerHTML = '我是登录浮窗'; div.style.display = 'none'; document.body.appendChild( div ); return div; }; var createSingleLoginLayer = getSingle( createLoginLayer ); document.getElementById( 'loginBtn' ).onclick = function(){ var loginLayer = createSingleLoginLayer(); loginLayer.style.display = 'block'; };
下面我们再试试创建唯一的iframe 用于动态加载第三方页面:
var createSingleIframe = getSingle( function(){ var iframe = document.createElement ( 'iframe' ); document.body.appendChild( iframe ); return iframe; }); document.getElementById( 'loginBtn' ).onclick = function(){ var loginLayer = createSingleIframe(); loginLayer.src = 'http://baidu.com'; };
在这个例子中,我们把创建实例对象的职责和管理单例的职责分别放置在两个方法里,这两
个方法可以独立变化而互不影响,当它们连接在一起的时候,就完成了创建唯一实例对象的功能,
看起来是一件挺奇妙的事情。