资讯专栏INFORMATION COLUMN

JavaScript设计模式与开发实践系列之单例模式

Airy / 3447人阅读

摘要:本系列为设计模式与开发实践作者曾探学习总结,如想深入了解,请支持作者原版单例模式实现单例模式单例模式的定义是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

本系列为《JavaScript设计模式与开发实践》(作者:曾探)学习总结,如想深入了解,请支持作者原版

单例模式 实现单例模式

单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如登录窗口,这个窗口是唯一的,无论我们点击多少次登录按钮,这个窗口只会被创建一次,那么这个窗口就适合用单例模式来创建。

要实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。代码如下:

</>复制代码

  1. var Singleton=function(name){
  2. this.name=name;
  3. this.instance=null;
  4. };
  5. Singleton.prototype.getName=function(){
  6. alert(this.name);
  7. };
  8. Singleton.getInstance=function(){
  9. if(!this.instance){
  10. this.instance=new Singleton(name);
  11. }
  12. return this.instance;
  13. };
  14. var a=Singleton.getInstance("sven1");
  15. var b=Singleton.getInstance("sven2");
  16. alert(a===b);//true

或者

</>复制代码

  1. var Singleton=function(name){
  2. this.name=name;
  3. };
  4. Singleton.prototype.getName=function(){
  5. alert(this.name);
  6. };
  7. Singleton.getInstance=(function(){
  8. var instance=null;
  9. return function(name){
  10. if(!instance){
  11. instance=new Singleton(name);
  12. }
  13. return instance;
  14. }
  15. })();

我们通过Singleton.getInstance来获取Singleton类的唯一对象,这种方式相对简单,但是不透明。跟以往通过new XXX的方式获取对象不同,这里偏要使用Singleton.getInstance来获取对象,所以这段代码的意义并不大。

透明的单例模式

我们现在的目标是实现一个透明的单例类。

</>复制代码

  1. var CreateDiv = (function() {
  2. var instance;
  3. var CreateDiv = function(html) {
  4. if (instance) {
  5. return instance;
  6. }
  7. this.html = html;
  8. this.init();
  9. return instance = this;
  10. };
  11. CreateDiv.prototype.init = function() {
  12. var div = document.createElement("div");
  13. div.innerHtml = this.html;
  14. document.body.appendChild(div);
  15. };
  16. return CreateDiv;
  17. })()
  18. var a = new CreateDiv("sven1");
  19. var b = new CreateDiv("sven2");
  20. alert(a===b);//true

虽然现在完成了一个透明的单例类的编写,但它同样有一些缺点。为了把instance封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回真正的Singleton构造方法,这增加了一些程序的复杂度,阅读起来也不是很舒服。
假设我们某天需要让这个单例类变成一个普通的类,即可以产生多个实例,那我们必须改写CreateDiv构造函数,这种修改会带来很多不必要的麻烦。

用代理实现单例模式

我们首先创建一个普通的CreateDiv类:

</>复制代码

  1. var CreateDiv = function(html) {
  2. this.html = html;
  3. this.init();
  4. };
  5. CreateDiv.prototype.init = function() {
  6. var div = document.createElement("div");
  7. div.innerHtml = this.html;
  8. document.body.appendChild(div);
  9. };

接下来引入代理类ProxySingletonCreate:

</>复制代码

  1. var ProxySingletonCreate = (function() {
  2. var instance;
  3. return function(html) {
  4. if (!instance) {
  5. instance = new CreateDiv(html);
  6. }
  7. return instance;
  8. }
  9. })();
  10. var a = new ProxySingletonCreate("sven1");
  11. var b = new ProxySingletonCreate("sven2");
  12. console.log(a === b);

这样一来,CreateDivProxySingletonCreate组合起来,实现了单例模式的效果。

JavaScript中的单例模式

前面提到的几种单例模式的实现,更多的是接近传统面向对象语言中的实现,单例对象从中创建而来。但JavaScript其实是一门无类的语言,所以生搬单例模式的概念并无意义。
单例模式的核心是:确保只有一个实例,并提供全局访问
全局变量不是单例模式,但在实际应用中,我们经常会把全局变量当成单例来使用。
例如:

</>复制代码

  1. var a = {};

全局变量可以满足上述的两个条件,但却存在许多问题:它很容易造成命名空间污染。作为普通的开发者,我们有必要减少全局变量的使用,一下几种方式可以相对降低全局变量带来的命名污染。

使用命名空间

最简单的方法依然是使用对象字面量方式:

</>复制代码

  1. var namespace1={
  2. a:function(){
  3. },
  4. b:function(){
  5. }
  6. };

我们还可以动态的创建命名空间:

</>复制代码

  1. var myApp = {};
  2. myApp.namespace = function(name) {
  3. var parts = name.split(".");
  4. var current = myApp;
  5. for (var i in parts) {
  6. if (!current[parts[i]]) {
  7. current[parts[i]] = {};
  8. }
  9. current = current[parts[i]];
  10. }
  11. };
  12. myApp.namespace("event");
  13. myApp.namespace("dom.style");
  14. console.log(myApp);
使用闭包封装私有变量

</>复制代码

  1. var user=(function(){
  2. var _name="sven",_age=29;
  3. return {
  4. getUserInfo:function(){
  5. return _name+"_"+_age;
  6. }
  7. }
  8. })();
惰性单例

惰性单例指的是在需要的时候才创建对象实例惰性单例是单例模式的重点,这种技术在实际开发中非常有用
我们先抽取出一个管理单例的逻辑对象:

</>复制代码

  1. var getSingle=function(fn){
  2. var result;
  3. return function(){
  4. return result||(result=fn.apply(this.arguments));
  5. }
  6. };

创建对象的方法fn被当做参数传入getSingle。

</>复制代码

  1. var getSingle = function(fn) {
  2. var result;
  3. return function() {
  4. return result || (result = fn.apply(this.arguments));
  5. };
  6. };
  7. var createLoginLayer = function() {
  8. var div = document.createElement("div");
  9. div.innerHTML = "我是登录窗";
  10. div.style.width = "100px";
  11. div.style.height = "100px";
  12. div.style.background = "red";
  13. document.body.appendChild(div);
  14. return div;
  15. };
  16. aa = getSingle(createLoginLayer);
  17. aa();

result变量因为身在闭包中,永远不会被销毁。在将来的请求中,如果result已经被赋值,那么他将返回这个值。
以下是演示地址:

惰性单例演示地址
其他演示地址

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78844.html

相关文章

  • javascript设计模式开发实践全书深度解析(一)单例模式

    摘要:所以程序在引入文件的时候用了单例模式,一个文件实例化一次,这种做法无疑是好的,但是也容易引起。在我们平时的开发过程中,可以借鉴这两种方式去缓存变量,节点等。 这一章作者讲了一个例子,就是在用单例模式生成一个dom节点,还要做到只有访问的时候才创建,后续访问直接用前面创建的。那么实际开发中我们会用到这个模式吗?现在我们基本都是用vue,react,angular开发,不太会直接去操作do...

    xioqua 评论0 收藏0
  • 设计模式单例模式

    摘要:最佳实践使用代理方式实现单例模式,使用一个代理函数来实现实单例例化原生的代码摘自设计模式与开发实践下来代理类测试函数返回版的用实现的单例模式代码已创建张三李四返回 说明:只要实例化一次,超过一次的实例化过程会返回之前实例化的结果,而不会在内存中再次写入新的实例对象。----类似于once。 需要遵守的原则:单一职责的原则,每一个类或者函数只负责一个功能。 最佳实践:使用代理方式实现单例...

    whinc 评论0 收藏0
  • 程序语言

    摘要:一面应该还问了其他内容,但是两次面试多线程面试问题和答案采访中,我们通常会遇到两个主题采集问题和多线程面试问题。多线程是关于并发和线程的。我们正在共享重要的多线程面试问题和答案。。 2016 年末,腾讯,百度,华为,搜狗和滴滴面试题汇总 2016 年未,腾讯,百度,华为,搜狗和滴滴面试题汇总 【码农每日一题】Java 内部类(Part 2)相关面试题 关注一下嘛,又不让你背锅!问:Ja...

    mtunique 评论0 收藏0
  • 程序语言

    摘要:一面应该还问了其他内容,但是两次面试多线程面试问题和答案采访中,我们通常会遇到两个主题采集问题和多线程面试问题。多线程是关于并发和线程的。我们正在共享重要的多线程面试问题和答案。。 2016 年末,腾讯,百度,华为,搜狗和滴滴面试题汇总 2016 年未,腾讯,百度,华为,搜狗和滴滴面试题汇总 【码农每日一题】Java 内部类(Part 2)相关面试题 关注一下嘛,又不让你背锅!问:Ja...

    stefan 评论0 收藏0
  • JS或Jquery

    摘要:大潮来袭前端开发能做些什么去年谷歌和火狐针对提出了的标准,顾名思义,即的体验方式,我们可以戴着头显享受沉浸式的网页,新的标准让我们可以使用语言来开发。 VR 大潮来袭 --- 前端开发能做些什么 去年谷歌和火狐针对 WebVR 提出了 WebVR API 的标准,顾名思义,WebVR 即 web + VR 的体验方式,我们可以戴着头显享受沉浸式的网页,新的 API 标准让我们可以使用 ...

    CatalpaFlat 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<