通俗讲,设计模式只是大量有经验的开发者在面对相同问题时,无数次使用过的解决方案。
可能很多人认为设计模式并没有多大用途,毕竟用普通的方法就能解决的问题,何必把代码逻辑搞得如此复杂,但从代码的可复用性和可维护性来看,好的设计的确可以节省大量的开发成本。优秀的 JavaScript 框架都运用了大量设计模式。
单例模式的核心是确保只有一个实例对象,并提供全局访问。
1、利用闭包封装需要频繁创建的对象
试想一下这样的场景,页面有一个登录窗,这个登录窗是唯一的,那么这个登录窗就非常适合用单例模式来创建。
我们可能会选择将这个登录窗定义在全局环境中:
例子:
<button id="login">登录</button> <script> var loginBox = (function() { var div = document.createElement("div"); div.innerHTML = "登录窗"; div.style.backgroundColor = "bisque"; div.style.display = "none"; document.body.appendChild(div); return div; })(); document.getElementById("login").onclick = function() { loginBox.style.display = "block"; }; </script>
例子中,我们将登录窗缓存在全局环境中并隐藏起来,当用户点击时,才开始展示。这种单例模式称为饿汉式单例,即实例在页面加载完成时就被创建。
登录窗定义在全局存在很多问题,容易造成命名空间污染,所以有必要减少全局变量的使用。最为理想的方式是将实例封装在闭包中,只暴漏外部访问的接口。
另外,我们打开页面,可能根本不会点击登录按钮,那么加载的数据只是白白浪费内存而已,因此,与饿汉式单例相反的、实际工作中使用更多的是懒汉式单例,即实例在第一次引用时才被创建,然后将结果缓存在闭包中。
例子:
<button id="login">登录</button> <script> var createLoginBox = (function() { var div; return function() { if (!div) { div = document.createElement("div"); div.innerHTML = "登录窗"; div.style.backgroundColor = "bisque"; document.body.appendChild(div); } return div; } })(); document.getElementById("login").onclick = function() { createLoginBox(); }; </script>
通用懒汉式单例
页面中,往往不止一种弹出窗,如果下次我们需要创建确认框,就去复制现有的单例方法,这明显不是明智的做法。
理想的做法是将单例方法中变化的部分抽离出来,然后再以回调函数的形式传回给单例。本例中,创建弹窗的代码是变化的,而管理单例的代码是不变的,因此我们将创建弹窗的代码单独放置在一个方法里,这样一来,原来的单例方法中,只剩下可以通用的代码。
例子:
<button id="login">登录</button> <button id="confirm">确认</button> <script> //管理单例的逻辑始终是一样的 var createSing = function() { var result; return function(fn) { if (!result) { result = fn(); } return result; } }; //创建登录窗的方法 var createLoginBox = function() { var div = document.createElement("div"); div.innerHTML = "登录窗"; div.style.backgroundColor = "#ffe4c4"; document.body.appendChild(div); return div; }; //创建确认窗的方法 var createConfirmBox = function() { var div = document.createElement("div"); div.innerHTML = "确认窗"; div.style.backgroundColor = "#ff5722"; document.body.appendChild(div); return div; }; //为登录窗创建一个实例 var login = createSing(); document.getElementById("login").onclick = function() { login(createLoginBox); }; //为确认窗创建一个实例 var confirm = createSing(); document.getElementById("confirm").onclick = function() { confirm(createConfirmBox); }; </script>
2、通过对象字面量减少全局变量的数量
在 JavaScript 中,全局变量会造成命名空间的污染,因此项目中都会加以限制。如果想要全局环境中只存在一个对象,并能够提供全局访问,单例模式是最佳选择。
例子:
<button>绑定事件</button> <script> //利用单例模式封装工具类(跨浏览器绑定事件) var EventUtil = { addHandler: function(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + type, handler); } else { element['on' + type] = handler; } }, removeHandler: function(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent('on' + type, handler); } else { element['on' + type] = null; } } }; //调用工具类 var btn = document.getElementsByTagName("button")[0]; EventUtil.addHandler(btn, "click", function() { alert("成功") }); </script>
*很多 JavaScript 插件、类库以及框架都会使用单例模式规范代码的各个功能模块。
如有错误,欢迎指正,本人不胜感激。