You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 开发者A写了一大段js代码
function addNum() {}
// 另一个开发者B开始写js代码
var addNum = '';
// A重新维护该js代码
addNum(); // 这个时候抛出错误:Uncaught TypeError: addNum is not a function
var Singleton = function(name) {
this.name = name;
}
Singleton.prototype.getName = function() {
alert(this.name);
}
// 利用闭包创建符合惰性的单例
Singleton.getInstance = (function(name)) {
var instance;
return function(name) {
if (!instance) {
instance = new Singleton(name);
}
}
})();
var a = Singleton.getInstance('test1');
var b = Singleton.getInstance('test2');
console.log(a === b); // true
透明的单例模式
// 反面的单例模式例子
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;
})();
var a = new CreateDiv('test1');
var b = new CreateDiv('test2');
参考文章
1. 从ES6重新认识JavaScript设计模式(一): 单例模式
单例模式
1. 什么是单例模式,特点是什么
单例模式是一种非常常用但却相对而言比较简单的设计模式。它是指在一个类中只能有一个实例,即使多次实例化该类,也只能返回第一次实例化后的对象。单例模式不仅能减少不必要的内存开销,并且在减少全局的函数和变量冲突也具有重要的意义。
单例模式能保证一个类只有一个实例,并且提供一个访问它的全局访问点。也就是说,在整个生命周期中,该对象的生产都始终是一个,不曾变化。
它的特点是:
2. 作用
3. 最简单的单例模式
即使目前我们对单例模式还比较模糊,但在我们日常开发中也早就已经使用过单例模式了。看下面的例子:
以对象字面量创建对象的方式在js开发中很常见。上面的例子就是以对象字面量的方式来封装了一些方法处理时间格式。全局只暴露了一个
timeTool
对象,在需要使用时,只需要采用timeTool.getISODate()
调用即可。timeTool
对象就是单例模式的体现。在JavaScript创建对象的方式十分灵活,可以直接通过对象字面量的方式实例化一个对象,而其他面向对象的语言必须使用类进行实例化。所以这里的timeTool
已经是一个实例,且ES6中let
和const
不允许重复声明的特性,确保了timeTool
不能被重新覆盖。4. 惰性单例
采用对象字面量创建单例只能适用于简单的应用场景,一旦该对象十分复杂,那么创建对象本身就需要一定的耗时,且该对象可能需要一些私有变量和私有方法。此时使用对象字面量创建单例就不再行得通了,我们还是需要采用构造函数的方式实例化对象。下面就是使用立即执行函数和构造函数的方式改造上面的
timeTool
工具。这时的
timeTool
实际上是一个函数,_instance作为实例对象最开始赋值为null
,init
函数是其构造函数,用于实例化对象,立即执行函数返回的是匿名函数,用于判断实例是否创建,只有当调用timeTool()
时进行实例化,这就是惰性单例的应用,不在js加载时就进行实例化创建,而是在需要的时候再进行单例的创建。如果再次调用,那么返回的永远是第一次实例化后的实例对象。单例模式的应用场景
1. 命名空间
一个项目往往都不止一个程序员来开发和维护,一个程序员很难去弄清楚另外一个程序员暴露在项目中的全局变量和方法。如果将变量和方法都暴露在全局中,变量冲突是难以避免的。就像下面的事故一样:
命名空间就是用来解决全局变量冲突的问题,我们完全可以只暴露一个对象名,将变量作为该对象的属性,将方法作为该对象的方法,这样就能大大减少全局变量的个数。
上面的代码中,
devA
和devB
就是两个命名空间,采用命名空间可以有效减少全局变量的数量,以此解决变量冲突的发生。2. 管理模块
上面讲到的
timeTool
对象时一个只用来处理时间的工具库,但是实际开发过程中的库可能会有多种多样的功能,例如处理ajax请求,操作dom或者处理事件。这个时候单例模式还可以用来管理代码库中的各个模块,如下所示:上面的代码库中有
ajax
,dom
和event
三个模块,用同一个命名空间devA
来管理。在进行相应的操作的时候,只需要devA.ajax.get()
进行调用即可。这样可以让库的功能更加清晰。ES6中的单例模式
1. ES6创建对象
ES6创建对象时引入了
class
和constructor
来创建。下面我们来看看如何使用ES6语法实例化苹果公司。2. ES6中创建的单例模式
显然,在全世界范围内,苹果公司有且只有一个。所以
appleCompany
应该是一个单例,现在我们使用ES6语法将constructor
改写为单例模式的构造器。3. ES6的静态方法优化代码
ES6为
class
提供了static
关键字定义的静态方法,我们可以将constructor
中判断是否实例化的逻辑放入一个静态方法getInstance
中,调用该静态方法获取实例,constructor
中只包含需要实例化的代码,这样可以增强代码的可读性、结构更加优化。传统OO语言的单例模式
这种形式的单例模式特点:
为了把instance封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回真正的Singleton构造方法,当然这增加了程序的复杂性。
CreateDiv构造函数做了两件事。
但是,这样创建出来的实例,不符合设计模式中的单一职责的概念。
项目实战应用
1.实现登陆弹框
登陆弹框在项目中是一个比较经典的单例模式,因为对于大部分网站不需要用户必须登陆才能浏览,所以登陆操作的弹框可以在用户点击登陆按钮后再进行创建。而且登陆框永远只有一个,不会出现多个登陆框的情况,也就意味着再次点击登录按钮后返回的永远是一个登录框的实例。
我们梳理一下登录框的流程,然后再实现。
因为5和6是登录框的实际项目逻辑,与单例模式关系不大,所以这里主要实现1-4步。
1. 给页面添加顶部导航栏的HTML代码
2. 使用ES6语法创建Login类
3. 给登录框添加注册点击事件
分析:
在上面的登录框中,实现了只创建一个Login类,但是却实现了一个并不简单的登录功能。在第一次点击登录按钮的时候,我们调用
Login.getInstance()
实例化了一个登录框,且在之后的点击中并没有重新创建新的登录框,只是移除掉了display:none
这个样式来显示登录框,节省了内存开销。总结
单例模式虽然简单,但是在项目中的应用场景却是相当多的,单例模式的核心是确保只有一个实例,并提供全局访问。就像我们只需要一个浏览器的
window
对象,jQuery的$
对象也不再需要第二个。由于JavaScript代码书写方式十分灵活,这也导致了如果没有严格的规范的情况下,大型的项目中JavaScript不利于多人协同开发,使用单例模式进行命名空间来管理模块是一个很好的开发习惯,能够有效的解决协同开发变量冲突的问题,灵活使用单例模式,也能够减少不必要的内存开销,提高使用体验。The text was updated successfully, but these errors were encountered: