1.单例模式

2017-02-24  本文已影响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:这个就是一个闭包
  */
新目标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();
上一篇 下一篇

猜你喜欢

热点阅读