乐趣区

关于前端:JavaScript-设计模式-单例模式

如果感觉文章不错,欢送关注、点赞和分享!
继续分享技术博文,关注微信公众号 👉🏻 前端 LeBron

在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案 —— 设计模式
设计模式准则:“找出程序中变动的中央,并将变动封装起来”

为什么要学习设计模式?

从事开发岗位也有一年多的工夫了,见识过陈年老我的项目,也从零到一搭建过一个我的项目。随着我的项目和业务的一直扩张,写下的代码如果没有进行设计,就慢慢变成了 emm … x 山,怎么写都不对劲,过段时间就想着重构。

人之所以能够走到食物链的顶端,是因为会学习、总结,会应用“名字”和“工具”。而设计模式并不是凭空创造进去的,是通过了大量的我的项目实际总结进去的对某种业务场景下的程序编写最佳实际,总结进去的解决方案,并给它取了个名字,就变成了一个设计模式。就好比篮球场上的战术,组织后卫常喊出打几号战术,而不是一个人一个人地指挥,简洁的代号往往比繁杂的形容更优雅。有可能你常常写程序的一种形式,能够形容进去,但不晓得它叫什么名字,有可能就是一种设计模式。所以咱们在学习的过程中,常常会有这种感触:ooooo!!这玩意儿我常常用,常常这么写,原来这个是 xx 模式!

最初,为什么要学设计模式呢?尽管有时候设计模式会使代码复杂度升高,减少开发的老本,但极大地升高了保护老本。就好比图书馆中的书如果无序地散落在各个角落,找起来如同海底捞针;而如果标号且归类、有序地放在指定书架上,找起来就容易了许多。设想一个场景,某天,你指着一段代码开始骂,这谁写的 x 山!鼠标移了下来,git 批改记录显示,哦!我写的,那没事了。连忙设计模式学起来,优雅地编写简洁、可复用、易保护的程序 ~

单例模式

1、保障一个类仅有一个实例,并提供一个拜访它的全局拜访点

2、次要解决一个全局应用的类频繁地创立和销毁,占用内存

实现单例模式

实现一个简洁的单例模式并不简单,无非就是将实例对象保存起来,下一次获取 / 创建对象的时候,间接返回之前创立的实例

最简略例模式

  • 闭包实现
  • ES6 实现
// 利用闭包实现
var Singleton = (function () {
  var instance;
  function Instance() {}
  return function () {if (!instance) {instance = new Instance();
    }
    return instance;
  };
})();

var single1 = Singleton();
var single2 = Singleton();

console.log(single1 === single2); // true


// ES6 实现
class Singleton {constructor() {if (!Singleton.instance) {Singleton.instance = this;}
    return Singleton.instance;
  }
}

const single1 = new Singleton();
const single2 = new Singleton();

console.log(single1 === single2); // true

惰性单例

惰性单例:初始化时不进行实例创立,到获取实例时才进行实例创立,相似前端的资源懒加载思维

// 非惰性单例
class Tool{}
class SingleTon{constructor (name,...args) {
    this.name = name
    if(!SingleTon.instance){SingleTon.instance = new Tool(args)
    }
  }

  getName(){return  this.name;}

  static  getInstance(){return  this.instance;}
}


// 惰性单例
class Tool{}
class SingleTon{constructor (name) {this.name = name}

  getName(){return  this.name;}

  static  getInstance(){if(!this.instance){this.instance = new Tool(arguments)
    }
    return  this.instance;
  }
}

通明单例和代理单例

  • 通明单例

    • 将实例的创立和办法的执行封装到了一个类中
    • 不太合乎“繁多职责准则”,代码不易读、不易改
  • 代理单例

    • 代理模式:将实例创立和办法拆散,委托进来
    • Person 作为一般类,能够创建对象实例
    • createPerson 作为单例创立代理,能够创立一个单例
    • 合乎“繁多职责准则”,代码易读、易改
 // 通明 单例
class createPerson{constructor (name) {if(createPerson.instance){return createPerson.instance;}
    this.name = name;
    this.init();
    return createPerson.instance = this;
  }

  init(){console.log('My name is',this.name)
  }
}
const p1 = new createPerson('lebron')
const p2 = new createPerson('james')
p1.init(); // My name is  lebron
p2.init(); // My name is  lebron


// 代理单例
class Person{constructor (name) {this.name = name;}
  init(){console.log('My name is',this.name)
  }
}

class createPerson{constructor (name) {if(!createPerson.instance){createPerson.instance = new Person(name)
    }
    return createPerson.instance;
  }
}
const p1 = new createPerson('lebron')
const p2 = new createPerson('james')
p1.init(); // My name is  lebron
p2.init(); // My name is  lebron

最终版本

每次实现一个类的单例模式都去 CV 反复的模板代码,不太合乎预期。

于是,咱们能够依据设计模式的准则“找出程序变动的中央,并将变动封装起来”

能够做如下革新

class SingleTon{constructor (fn) {
    let singleInstance;
    function singleConstructor(...args){
      // 第一次实例化
      if(!singleInstance){singleInstance = new fn(...args);
      }
      // 屡次实例化间接返回
      return singleInstance;
    }

    singleConstructor.prototype = Object.create(fn.prototype);
    return singleConstructor;
  }
}

class Person{constructor (name) {this.name = name;}
  init(){console.log('My name is',this.name)
  }
}
const createPerson = new SingleTon(Person);
const p1 = new createPerson('lebron');
const p2 = new createPerson('james');
p1.init(); // My name is  lebron
p2.init(); // My name is  lebron
console.log(p1 === p2)  // true

class Tool{constructor (number) {this.number = number;}

  init(){console.log('This is tool', this.number)
  }
}
const createTool = new SingleTon(Tool);
const t1 = new createTool(1);
const t2 = new createTool(2);
t1.init(); // This is tool  1
t2.init(); // This is tool  1
console.log(t1 === t2); // true

单例模式的利用场景

登录窗口

登录窗口在咱们的软件中很常见,也很适宜应用单例模式创立(全局惟一、频繁应用)

应用单例模式创立能够防止屡次节点创立和销毁

class Login {constructor() {this.element = document.createElement('div');
    this.element.innerHTML = `
           用户名 <input name="username"/>
           明码 <input name="password"/>
           <input type="submit" value="登录"/>
          `;
    this.element.style.cssText = `width:100px;height:100px;position:absolute;left:50%;top:50%;margin-top:-50px;margin-left:-50px;display:none`;
    document.body.appendChild(this.element);
  }
  show() {
    this.element.style.display = 'block';
    console.log('show');
  }
  hide() {
    this.element.style.display = 'none';
    console.log('hide');
  }
}

// 采纳上文最终版本创立单例代码
const createLogin = new SingleTon(Login);
const loginInstance = createLogin();

document.getElementById('show-btn').addEventListener('click', function () {loginInstance.show();
});
document.getElementById('hide-btn').addEventListener('click', function () {loginInstance.hide();
});

Store

全局状态治理,全局惟一实例,例如 Vuex / Redux / Mobx

// 已省略一些无关代码
if (!Vue && typeof window !== 'undefined' && window.Vue) {install(window.Vue)
}

function install (_Vue) {if (Vue && _Vue === Vue) {console.error('[vuex] already installed. Vue.use(Vuex) should be called only once.')
    return
  }
  Vue = _Vue
}

Jquery

Jquery 也是利用的单例模式,尽管它曾经慢慢淡出了咱们的视线 😂

if(window.jQuery!=null){return window.jQuery;}else{//init~~~~~~~}

总结

应用场景

  • 保障一个类仅有一个实例,并提供一个拜访它的全局拜访点

长处

  • 在内存里只有一个实例,缩小了内存的开销,尤其是频繁地创立和销毁实例
  • 防止对资源的多重占用

闲聊

上半年始终在忙毕业的事件,而后也劳动了一段时间,曾经大半年没写技术文章了。

近期正式入职了,开始逐渐调整到一个比拟好的学习和工作状态。制订一些学习打算,复原更新文章(很久没写还是有点不太习惯哈哈

第一个系列《设计模式》第一篇墨迹了半天,但也终于产出了。后续会继续更新,更新频率和进度看闲暇工夫状况了哈哈,欢送关注~

一个还在致力的前端工程师,持续加油!

继续分享技术博文,欢送关注!

  • 掘金:前端 LeBron
  • 知乎:前端 LeBron
    继续分享技术博文,关注微信公众号👇🏻
退出移动版