美文网首页
1.单例模式

1.单例模式

作者: 昵称最难起 | 来源:发表于2017-02-24 00:19 被阅读5次

定义:保证一个类(构造函数)仅有一个实例,并提供一个访问它的全局访问点。

1.作者举了最简单的两个例子:

//首先在这里定义了一个构造函数,singleton的意思是单身汉。

  var Singleton  = function (name) {
    this.name = name;
    this.instance = null;
    //instance的意思是例子
  };

  Singleton.prototype.getName = function () {
    alert(this.name);
  };

//下面要注意,getInstance方法是直接写在Singleton身上的。

//而且,函数里面的this也是指向Singleton的。

//我读到这里的时候会下意识的这样想:不都是说构造函数中的this是指向实例的么?

//其实是这样的,大家常说的构造函数的this指向实例,是因为new的关系。

//new的内部实现,把构造函数apply给了实例,所以才会改变this的指向。

//所以下面的代码this.instance是可以取到的。

  Singleton.getInstance = function (name) {
    if (!this.instance) {
      this.instance = new Singleton(name)
    }
    return this.instance;
  }

  var a = Singleton.getInstance('cyw');
  var b = Singleton.getInstance('mn');

  /* 在这里,我们来看一下,这会产生什么效果? */
  console.log(a === b); // true
  a.getName();//'cyw'
  b.getName();//'cyw'

  /*
 我们发现,不管getInstance方法执行了几次,他都只是返回第一次构造的实例。
这是因为,this.instance充当了一个标记的角色。
它在第一次执行之后,就永远的被改写成了实例。
以后再怎么调用,都只是返回那个实例。
 */

再来看下一个例子,和第一个很像。

 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;
    }
  })();
  var a = Singleton.getInstance('cyw');
  var b = Singleton.getInstance('mn');
  console.log(a === b);// true
  /*
   * 这里的实现方式略有不用
    * 在第三个函数直接使用了一个立即执行函数进行包裹
    * 所以拿到的是里面那个匿名函数
    * 匿名函数又使用了立即执行函数里面的变量instance:这个就是一个闭包
  */
  • 读到这里我们学会了,判断 + 改写 + return可以实现这种效果。
  • 但是作者又说这种实现方式很不好
  • 这个构造函数的'透明性'差


    b8389b504fc2d5624fce51daef1190ef77c66cc7.jpg
  • 还要使用Singleton.getInstance方法才能获得实例。
新目标1:改写这个函数,要求直接使用构造函数就能获得单例
写一个在页面创建一个唯一的DIV的例子。 

var CreateDiv  =(function () {
   var instance;

   var CreateDiv = function (html) {
     if (instance) {
       return instance;
     }
     this.html = html;
     this.init();
     return instance = this;//这句话就是intance = this;return instance;
   };

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

  var a = new CreateDiv('hello');
  var b = new CreateDiv('world');
  console.log(a === b);
/*
 * 读这段代码,我们可以发现 ,就是把本来外面的方法,拿到构造函数里面了。
  * 还是利用的立即执行函数
  * 外面的函数名和里面的函数名字一样可能会带来理解上的误区,你当然可以把里面的换一个名字
  * 真正的构造函数写在里面 ,外面的构造函数拿到的是里面的构造函数的引用
  * 让 instance = this; 因为this就是实例 */
  • 读到这里,作者又觉得这个方法的实现有问题
  var CreateDiv = function (html) {
     if (instance) {
       return instance;
     }
     this.html = html;
     this.init();
     return instance = this;//这句话就是intance = this;return instance;
   };
这个函数同时做了两件事情:

1.创建对象和初始化init方法(我们可以注意到定义在原型上的init方法,是在构造函数中被调用的。)
2.确保了只有一个实例。

这违反了"单一职责原则",不方便复用,而且看起来很怪。
ps.注意大牛的思考方式,这是最重要的。

我们使用代理类的方式来解决这种情况:
  var CreateDiv = function (html) {
    this.html = html;
  };
  CreateDiv.prototype.init = function () {
    var div = document.createElement('div');
    div.innerHTML = this.html;
    document.body.appendChild(div);
  }
// 首先这就是一个普通的类,他是实现的是职责就是创建div
//现在我们来引入代理类,来实现单例模式

  var ProxySingletonCreateDiv = (function () {
    var instance;
    return function (html) {
      if (!instance) {
        return new CreateDiv(html);
      }
      return instance;
    }
  })()

 var a = new ProxySingletonCreateDiv('hello');
  var b = new ProxySingletonCreateDiv('world');
  console.log(a === b);//true

// 这几句代码写来写去,好像都在哪见过,但是从新的组合和设计确实实现了作者口中的优化。

javascript中的单例模式

在javascript中我们并不需要如此的依赖类的概念
其实
var a = {};
就是一个单例模式的例子。
首先它是独一无二的。
其次他还是全局可以访问的。
但是a这个引用太危险了,他随时可能在编程的过程中被改写。
  • 为了避免这种情况,我们可以使用下面两种小技巧。
1.命名空间
  var namespace = {
    a: function () {
      console.log(1);
    },
    b: function () {
      console.log(2);
    }
  }
我们把a和b写成对象的属性,这样就使其远离了全局作用域。

好玩的东西: 动态创建命名空间
 var MyApp = {};
  MyApp.namespace = function (name) {
    var current = MyApp;
    var parts = name.split('.');
    for (var i in parts) {
      if (!current[parts[i]]) {
        current[parts[i]] = {}
      }
      current = current[parts[i]];
    }
  };
  MyApp.namespace('one');
  MyApp.namespace('two.three.four');  
  MyApp.namespace('two.three.five'); 

  相当于:
  var MyApp = {
    one: {},
    two: {
      three: {
        four: {},
        five: {}
      }
    }
  }
2.使用闭包封装私有变量
var user = (function () {
  var _name = 'cyw';
  var _age = '25';
  return {
      getUserInfo: function () {
        return _age + '-' + _name;
      }
    }
})()
下划线'_'是私有变量的常用命名规则。

惰性单例

惰性的意思就是,需要的时候才创建实例

以创建登录窗口为例
我们把单例模式不变的逻辑单独提取出来
var getSingle = function (fn) {
  var result;
  return function () {
    return result || (result = fn.apply(this, arguments));
  }
}

//再写一个简单不过的创建div的方法。
var createLoginLayer = function () {
  var div = document.createElement('div');
  div.innerHTML = '我是登录窗口';
  div.style.display = 'none';
  document.body.appendChild(div);
  return div;
}

/* 两个方法都是单一职能,结合起来就形成了惰性单例模式 */
var createSingleLoginLayer = getSingle(createLoginLayer);
var div = createSingleLoginLayer();
  • 希望这个读书笔记可以坚持下来。
  • 写第一篇就有一种感受,现在的mvvm框架功能太强太。
  • 如果用框架来写,文中的好多技巧都无处发挥了。
  • 一个vue的v-if还用什么单例模式(也可能是我现在认识比较浅薄)
  • 虽然能看的懂代码,但我希望更能学会大牛的思考方式,希望能得到进步
  • 还要继续找工作,初级前端的工作太难找了。

相关文章

网友评论

      本文标题:1.单例模式

      本文链接:https://www.haomeiwen.com/subject/hqzbwttx.html