浅析Javascript单例模式

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点 。就想我们在开发中有些对象只需要一个,例如window对象。

1. 实现单例模式

var Singleton = function( name ) {
    this.name = name;
};
Singleton.prototype.getName = function() {
    alert ( this.name );
};
Singleton.getInstance = function( name ) {
    if ( !this.instance ) {   //这里的this指的是Singleton,所以两次调用的都是同一个this
        this.instance = new Singleton( name );
    }
    return this.instance;
};
var a = Singleton.getInstance( ‘sven1‘ );
var b = Singleton.getInstance( ‘sven2‘ );
alert ( a === b ); // true      

我们通过 Singleton.getInstance 来获取 Singleton 类的唯一对象,这种方式相对简单,这种方式非常类似于Java的单例模式实现,但有 一个问题,就是增加了这个类的“不透明性”,Singleton 类的使用者必须知道这是一个单例类, 跟以往通过 new的方式来获取对象不同,这里偏要使用 Singleton.getInstance 来获取对象。 那么有没有办法直接New出来的就是一个单例的对象呢?

2.透明的单例模式

var Singleton = (function() {
  var instance;
  var Singleton = function(name) {
    if ( instance ) {
      return instance;
    }
    this.name = name;
    return instance = this;
  };
  Singleton.prototype.getName = function() {   //就近原则,用的是上面的Singleton
    alert ( this.name );
  };
  return Singleton;
})();
var a = new Singleton( ‘sven1‘ );
var b = new Singleton( ‘sven2‘ );
console.log(a == b)

虽然现在完成了一个透明的单例类的编写,但它同样有一些缺点。

为了把 instance 封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回,真正的 Singleton 构造方法,这增加了一些程序的复杂度,阅读起来也不是很舒服。 所以说有利有弊吧

3.Javascript中的单例模式

前面提到的几种单例模式的实现,更多的是接近传统面向对象语言中的实现,单例对象从 “类”中创建而来。在以类为中心的语言中,这是很自然的做法。比如在 Java 中,如果需要某个对象,就必须先定义一个类,对象总是从类中创建而来的。

但 JavaScript 其实是一门无类(class-free)语言(虽然ES6中有了类的概念),也正因为如此,生搬单例模式的概念并无 意义。在 JavaScript 中创建对象的方法非常简单,既然我们只需要一个“唯一”的对象,为什 么要为它先创建一个“类”呢?

单例模式的核心是确保只有一个实例,并提供全局访问。

问题:现在有一个需求是点击页面上的登录按钮,显示一个登录弹窗,很显然这个弹窗是唯一的,也就是单例的

(1)

<html>
  <body>
    <button id="loginBtn">登录</button>
  </body>
  <script>
  var loginLayer = (function() {
    var div = document.createElement( ‘div‘ );
    div.innerHTML = ‘我是登录浮窗‘;
    div.style.display = ‘none‘;
    document.body.appendChild( div );
    return div;
  })();
  document.getElementById( ‘loginBtn‘ ).onclick = function(){
  loginLayer.style.display = ‘block‘; };
  </script>
 </html>

上面解决方案是在页面加载完成的时候便创建好这个 div 浮窗,这个浮窗一开始肯定是隐藏状态的,当用户点击登录按钮的时候,它才开始显示,但是有时候我们根本不需要进行登录操作,如果一开始就被创建好其实是一种浪费资源的行为,DOM资源可是很珍贵的

(2)

<html>
  <body>
    <button id="loginBtn">登录</button>
  </body>
  <script>
  var createLoginLayer = function() {
    var div = document.createElement( ‘div‘ );
    div.innerHTML = ‘我是登录浮窗‘;
    div.style.display = ‘none‘;
    document.body.appendChild( div );
    return div;
  };
  document.getElementById( ‘loginBtn‘ ).onclick = function(){
    var loginLayer = createLoginLayer();
    loginLayer.style.display = ‘block‘;
  };
  </script>
</html>

上面的方法,虽然做到了点击时才创建,但失去了单例的效果。当我们每次点击登录按钮的时候,都会 创建一个新的登录浮窗 div。虽然我们可以在点击浮窗上的关闭按钮时(此处未实现)把这个浮 窗从页面中删除掉,但这样频繁地创建和删除节点明显是不合理的,也是不必要的。

(3)

<html>
  <body>
    <button id="loginBtn">登录</button>
  </body>
  <script>
  var createLoginLayer = (function(){
    var div;
    return function() {
      if ( !div ) {
        div = document.createElement( ‘div‘ );
        div.innerHTML = ‘我是登录浮窗‘;
        div.style.display = ‘none‘;
        document.body.appendChild( div );
      }
      return div;
    }
  })();
  document.getElementById( ‘loginBtn‘ ).onclick = function(){
    var loginLayer = createLoginLayer();
    loginLayer.style.display = ‘block‘;
  };
  </script>
</html>

上述方法,用一个变量来判断是否已经创建过登录浮窗,和上面上面实现单例的类的原理是一样的

4.通用的单例模式

如果在项目中用到了很多单例的情况,如果每个都做判断,其实是相当复杂的事情,但能不能写一个通用的方法,经过这个方法处理的就是单例的

<html>
  <body>
    <button id="loginBtn">登录</button>
  </body>
  <script>
  var getSingle = function(fn){
    var result;
    return function() {
      return result || ( result = fn .apply(this, arguments ) );
    }
  };
  var createLoginLayer = function() {
    var div = document.createElement( ‘div‘ );
    div.innerHTML = ‘我是登录浮窗‘;
    div.style.display = ‘none‘;
    document.body.appendChild( div );
    return div;
  };
  var createMask = function() {
    var div = document.createElement( ‘div‘ );
    div.innerHTML = ‘mask‘;
    div.style.display = ‘none‘;
    document.body.appendChild( div );
    return div;
  };
  var createSingleLoginLayer = getSingle( createLoginLayer );
  var createMask = getSingle( createMask );
  document.getElementById( ‘loginBtn‘ ).onclick = function(){
    var loginLayer = createSingleLoginLayer();
    var mask = createMask();
    loginLayer.style.display = ‘block‘;
    mask.style.display = ‘block‘;
  };
  </script>
</html>

我们发现在开发中并不会单独使用弹出窗,遮罩层和弹出窗是经常结合在一起使用,上面的方法就能使弹出窗和遮罩层都是单例的,其中核心的方法就是getSingle

在getSingle函数中,创建对象的方法 fn 被当成参数动态传入 getSingle 函数 ,result 变量因为身在闭包中,它永远不会被销毁。在将来的请求中,如果 result 已经被赋值,那么它将返回这个值 。

5. ES6中的单例模式

class SingletonApple {
  constructor(name, creator, products) {
      this.name = name;
      this.creator = creator;
      this.products = products;
  }
  //静态方法
  static getInstance(name, creator, products) {
    if(!this.instance) {
      this.instance = new SingletonApple(name, creator, products);
    }
    return this.instance;
  }
}
let appleCompany = SingletonApple.getInstance(‘苹果公司‘, ‘乔布斯‘, [‘iPhone‘, ‘iMac‘, ‘iPad‘, ‘iPod‘]);

ES6有类,也有静态方法,实现单例模式也显得非常的简单,学过Java的同学能看出几乎和Java的实现一模一样,直接调用SingletonApple的静态方法getInstance就能获得一个单例对象。ES6能不能直接new一个单例的对象出来呢,答案当然是可以的

class SingletonApple {
  constructor(name, creator, products) {
      this.name = name;
      this.creator = creator;
      this.products = products;
  }
  //静态方法
  static getInstance(name, creator, products) {
    if(!this.instance) {
      this.instance = new SingletonApple(name, creator, products);
    }
    return this.instance;
  }
}

Javascript单例模式介绍到此结束,有不对的地方请及时指出

原文地址:https://www.cnblogs.com/mianbaodaxia/p/9118660.html

时间: 2024-09-29 16:21:01

浅析Javascript单例模式的相关文章

浅析JavaScript和PHP中三个等号(===)和两个等号(==)的区别

先做个简单的介绍,让先有个直观的认识 == equality 等同 === identity 恒等 == 两边值类型不同的时候,要先进行类型转换,再比较. === 不做类型转换,类型不同的一定不等. 举例说明: "1" == true 类型不同,"=="将先做类型转换,把true转换为1,即为 "1" == 1: 此时,类型仍不同,继续进行类型转换,把"1"转换为1,即为 1 == 1: 此时,"==" 左

javascript单例模式(懒汉 饿汉)

第一种:懒汉模式 var Singleton=(function(){ var instantiated; //比较懒,在类加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢 function init(){ /*这里定义单例代码*/ return{ publicMethod:function(){ console.log('helloworld'); }, publicProperty:3 }; } return{ getInstance:function(){ if(!insta

浅析JavaScript闭包

闭包和原型是javascript语言的两大特点,上篇博文<浅析JavaScript原型>中已经总结了原型 ,今天就总结一下闭包的相关知识. 前言 在开始闭包之前,需要先介绍一下匿名函数和JavaScript垃圾回收机制这两个概念. 匿名函数 匿名函数,很容易理解,就是没有名字的函数. //普通函数 function box(){ return 'This's just a test'; } //匿名函数的架构思想,但是这样写会报错 function (){ return 'This's jus

浅析 JavaScript 中的 函数 currying 柯里化

原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果.因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程. 柯里化一个求和函数 按照分步求值,我们看一个

[转] JavaScript 单例模式

定义 确保一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式使用的场景 比如线程池.全局缓存等.我们所熟知的浏览器的window对象就是一个单例,在JavaScript开发中,对于这种只需要一个的对象,我们的实现往往使用单例. 实现单例模式 (不透明的) 一般我们是这样实现单例的,用一个变量来标志当前的类已经创建过对象,如果下次获取当前类的实例时,直接返回之前创建的对象即可.代码如下: // 定义一个类 function Singleton(name) { this.name = na

javascript 单例模式

单例模式是设计模式里最基本也是最常用的模式之一,其特点是单体对象只存在一份实例,这样你就能确保在你的所有代码里面都使用着同样的全局资源. 在Java里面实现单例模式的方法是首先将构造函数私有化,在内部创建一个私有的静态变量保存实例,然后通过一个静态方法(一般取名为getInstance)将实例提供给外部访问.因为构造函数的私有化,所以外部无法对类进行实例化,而只能通过getInstance获取,所以由始到终只有那一个静态实例. 在Javascript里面我们同样可以借鉴这种思路. (一)使用对象

浅析JavaScript访问对象属性和方法及区别

属性是一个变量,用来表示一个对象的特征,如颜色.大小.重量等:方法是一个函数,用来表示对象的操作,如奔跑.呼吸.跳跃等. 在JavaScript中通常使用"."运算符来存取对象的属性的值.或者使用[]作为一个关联数组来存取对象的属性. 对象的属性和方法统称为对象的成员. 访问对象的属性 在JavaScript中,可以使用" . "和" [ ] "来访问对象的属性. 1. 使用" . "来访问对象属性 语法: objectNam

javascript单例模式的理解

阅读目录 理解单例模式 使用代理实现单例模式 理解惰性单例 编写通用的惰性单例 单例模式使用场景 回到顶部 理解单例模式 单例模式的含义是: 保证一个类只有一个实例,并提供一个访问它的全局访问点.实现的方法是:使用一个变量来标志当前是否已经为某个类创建过对象,如果创建了,则在下一次获取该类的实例时,直接返回之前创建的对象,否则就创建一个对象.这就确保了一个类只有一个实例对象. 比如如下代码是一个简单的单例模式代码实例: var Singleton = function(name){ this.n

【JavaScript】浅析JavaScript对象如何添加属性和方法

向JavaScript类中添加属性和方法,最直观的做法就是在类中定义属性和方法.JavaScript是一门弱语言,除了直接定义还可以用prototype来添加. 下面介绍从外部向JavaScript添加属性和方法的四种方法,首先定义一个类 function MyClass(){} 1,使用类名添加属性 MyClass.prototype.myname="吴兴国01"; 2,使用类名添加方法 MyClass.prototype.myfunc=function(){alert("