单例模式也称为单体模式, 规定一个类只有一个实例, 并且提供可全局访问点;
在读这篇文章之前, 也许你对单例模式的概念感到模糊或者不清楚, 但是其实在日常的开发中你肯定用到过单例模式;
JavaScript 中没有类的定义, 单例模式的特点是 "唯一" 和 "全局访问", 那么我们可以联想到 JavaScript 中的全局对象, 利用 ES6 的 let 不允许重复声明的特性, 刚好符合这两个特点; 是的, 全局对象是最简单的单例模式;
- let obj = {
- name:"咸鱼",
- getName:function(){}
- }
上述代码中可以知道 obj 就是一个单例, 因为 obj 刚好就符合单例模式的两大特点:"唯一" 和 "可全局访问";
但是我们并不建议这么实现单例, 因为全局对象 / 全局变量会有一些弊端:
污染命名空间 (容易变量名冲突)
维护时不容易管控 (搞不好就直接覆盖了)
简单版单例模式:
分析: 只能有一个实例, 所以我们需要使用 if 分支来判断, 如果已经存在就直接返回, 如果不存在就新建一个实例;
- let Singleton = function(name){
- this.name = name;
- this.instance = null;
- }
- Singleton.prototype.getName = function(){
- console.log(this.name);
- }
- Singleton.getInstance = function(name){
- if(this.instace){
- return this.instance;
- }
- return this.instance = new Singleton(name);
- }
- let winner = Singleton.getInstance("winner"); //winner
- console.log(winner.getName());
- let sunner = Singleton.getInstance("sunner"); //winner
- console.log(sunner.getName())
上面代码中我们是通过一个变量 instance 的值来进行判断是否已存在实例, 如果存在就直接返回 this.instance, 如果不存在, 就新建实例并赋值给 instance;
但是上面的代码还是存在问题, 因为创建对象的操作和判断实例的操作耦合在一起, 并不符合 "单一职责原则";
改良版:
思路: 通过一个闭包, 来实现判断实例的操作;
闭包警告: 不理解闭包的同学请先学习闭包 https://www.cnblogs.com/dengyao-blogs/p/11475575.html
- let CreateSingleton = (function(){
- let instance = null;
- return function(name){
- this.name = name;
- if(instance){
- return instance
- }
- return instance = this;
- }
- })()
- CreateSingleton.prototype.getName = function(){
- console.log(this.name);
- }
- let winner = new CreateSingleton("winner"); //winner
- console.log(winner.getName());
- let sunner = new CreateSingleton("sunner"); //winner
- console.log(sunner.getName())
代理版单例模式:
通过代理的形式, 将创建对象的操作和实例判断的操作进行解耦拆分, 实现更小粒度的划分, 符合 "单一职责原则";
- let ProxyCreateSingleton = (function(){
- let instance = null;
- return function(name){
- if(instance){
- return instance
- }
- return instance = new Singlton(name);
- }
- })();
- let Singlton = function(name){
- this.name = name;
- }
- Singlton.prototype.getName = function(){
- console.log(this.name);
- }
- let winner = new ProxyCreateSingleton("winner");
- console.log(winner.getName());
- let sunner = new ProxyCreateSingleton("sunner");
- console.log(sunner.getName());
上面的代码中, ProxyCreateSingleton() 只负责判断实例, Singlton 只负责创建对象和赋值;
惰性单例模式
我们经常会有这样的场景: 页面多次调用都有弹窗提示, 只是提示内容不一样;
这个时候我们可以立马想到是单例模式, 弹窗就是单例实例, 提示内容是参数传递; 我们可以用惰性单例模式来实现它;
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>
- Document
- </title>
- </head>
- <body>
- <div id="loginBtn">
- 有梦想的咸鱼
- </div>
- </body>
- <script>
- let getSingleton = function(fn) {
- var result;
- return function() {
- return result || (result = fn.apply(this, arguments)); // 确定 this 上下文并传递参数
- }
- }
- let createAlertMessage = function(HTML) {
- var div = document.createElement('div');
- div.innerHTML = HTML;
- div.style.display = 'none';
- document.body.appendChild(div);
- return div;
- }
- let createSingleAlertMessage = getSingleton(createAlertMessage);
- document.getElementById('loginBtn').onclick = function() {
- let alertMessage = createSingleAlertMessage('看来真的是个咸鱼');
- alertMessage.style.display = 'block';
- }
- </script>
- </HTML>
惰性单例是指的是页面开始加载的时候我们的实例是没有进行创建的, 是当我们点击页面的 div 之后才开始创建实例 (按需创建), 这可以提高我们的网页性能, 加快我们的页面渲染速度;
来源: https://www.cnblogs.com/dengyao-blogs/p/11652566.html