最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 『设计模式』—— 单例模式

    正文概述 掘金(a逼c迪e诶扶鸡)   2020-12-18   526

    定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点

    核心:确保只有一个实例,并提供全局访问

    单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的 window 对象等。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建

    实现单例模式

    要实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象
    var Singleton = function( name ){
        this.name = name;
        // 实例
        this.instance = null;
    };
    Singleton.prototype.getName = function(){
        console.log(this.name)
    }; 
    Singleton.getInstance = function( name ){
        // 如果存在实例,则直接跳过
        if ( !this.instance ){
            this.instance = new Singleton( name );
        }
        return this.instance;
    }; 
    var a = Singleton.getInstance( 'sven1' );
    var b = Singleton.getInstance( 'sven2' );
    
    console.log( a === b ); // true
    a.getName(); // sven1
    b.getName(); // sven1
    

    或者

    var Singleton = function( name ){
        this.name = name;
    };
    Singleton.prototype.getName = function(){
        console.log(this.name)
    };
    // 使用闭包来保存实例对象
    Singleton.getInstance = (function(){
        var instance = null;
        return function( name ){
            if ( !instance ){
           	    instance = new Singleton( name );
            }
            return instance;
       }
    })();
    
    console.log( a === b ); // true
    a.getName(); // sven1
    b.getName(); // sven1
    

    我们通过 Singleton.getInstance 来获取 Singleton 类的唯一对象,这种方式存在一个问题,就是增加了这个类的“不透明性”,Singleton 类的使用者必须知道这是一个单例类, 必须使用 Singleton.getInstance 来获取对象,不能通过以往new来获取对象

    用代理实现单例模式

    // 构造函数
    var CreateSingleton = function( name ){
        this.name = name;
        this.init(); 
    }
    CreateSingleton.prototype.init = function(){
        // 这里执行初始化方法
        console.log( this.name )
    } 
    /**
     * 引入代理类-proxyClass来负责管理单例
     * 这里使用了自执行的匿名函数和闭包,将变量封装在闭包的内部
     */
    var proxyClass = (function(){
        var instance; 
        return function( name ){
             // 如果存在实例,则跳过
             if ( !instance ){
             	instance = new CreateSingleton( name );
             } 
             return instance;
        }
    })(); 
    
    var a = new proxyClass('singleton1'); 
    var b = new proxyClass('singleton2');
    console.log( a === b ); // true
    

    通过引入代理,我们可以直接通过new来获取单例对象,并且把负责管理单例的逻辑转移到了代理类-proxyClass中,这样一来CreateSingleton就变成了一个普通的类,它跟proxyClass组合起来可以达到单例模式的效果

    JavaScript 中的单例模式

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

    但JavaScript 其实是一门无类语言,在 JavaScript 中创建对象的方法非常简单,直接创建一个变量对象,反正我们只需要一个“唯一”的对象,没必要为它先创建一个“类”呢?这无异于穿棉衣洗澡

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

    var a = {};
    

    之后我们就有点迷惑了,既然如此,那我们直接创建一个唯一的变量就是了,但是全局变量存在很多问题,它很容易造成命名空间污染,所以我们要用几种方式来降低全局变量带来的命名污染

    ① 使用命名空间

    适当地使用命名空间,并不会杜绝全局变量,但可以减少全局变量的数量
    var namespace = {
        a: function(){
           alert (1);
        },
        b: function(){
           alert (2);
        }
    }; 
    

    ② 使用闭包封装私有变量

    这种方法把一些变量封装在闭包的内部,只暴露一些接口跟外界通信
    // 我们用下划线来约定私有变量__name 和__age,它们被封装在闭包产生的作用域中,外部是访问不到这两个变量的,这就避免了对全局的命令污染
    var user = (function(){
        var __name = 'sven',
            __age = 29;
        return {
            getUserInfo: function(){
                return __name + '-' + __age;
            }
        }
    })(); 
    

    惰性单例

    惰性单例指的是在需要的时候才创建对象实例

    实际上在开头的代码中头就使用过这种技术, instance 实例对象总是在我们调用 Singleton.getInstance 的时候才被创建,而不是在页面加载好 的时候就创建,代码如下:

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

    不过这是基于“类”的单例模式, JavaScript 中并不适用,接下来展示js中与全局变量结合实现惰性的单例

    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 LoginLayer = getSingle( createLoginLayer )
    //打开浮窗
    LoginLayer().style.display = 'block'
    //关闭浮窗
    LoginLayer().style.display = 'none'
    

    但是现在都2020年了,都用框架了,已经很少有完全直接操作DOM的时候了,但是设计模式还是可以用的

    Vue中使用惰性单例

    惰性单例有两大优点:

    单例 —— 只被创建一次

    惰性 —— 只加载已“缓存好的”

    因此在Vue中我们可以这样优化代码:

    ①v-if主动控制首次不加载,需要时才加载

    ②v-show首先默认为true,只在v-if为ture之后再控制显示与隐藏

    <template>
      <div>
        <el-button @click="isLoad = true">点击开始加载dialog</el-button>
        <!-- element的对话框组件 -->
        <el-dialog
          
          :visible.sync="dialogVisible"
          width="30%"
          v-if="isLoad"
        >
          <span>这是一段信息</span>
          <span slot="footer" class="dialog-footer">
            <el-button @click="dialogVisible = false">取 消</el-button>
            <el-button type="primary" @click="dialogVisible = false"
              >确 定</el-button
            >
          </span>
        </el-dialog>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          isLoad:false,
          dialogVisible: false,
        };
      },
    };
    </script>
    

    这里是通过点击将isLoad变为true才开始加载el-dialog的,el-dialog的隐藏和显示就是用v-show来控制,这种优化适用于一些占很大资源却对于用户不常有的功能,导致加载时间会被无意义地拖长的情况

    Vuex中的单例模式

    在项目中引入Vuex:
    // 引入vuex插件
    import Vuex from 'vuex'
    // 安装vuex插件
    Vue.use(Vuex)
    
    // 创建vuex的实例
    var store =  new Vuex.Store({})
    // 将store注入到Vue实例中
    new Vue({
        el: '#app',
        store
    })
    

    通过调用Vue.use()方法,安装 Vuex 插件。Vuex 插件是一个对象,它在内部实现了一个 install 方法,这个方法会在插件安装时被调用,从而把 Store 注入到Vue实例里去

    let Vue // Vue的作用和上面的instance一样
    ...
    
    export function install (_Vue) {
      // 判断传入的Vue实例对象是否已经被install过(是否有了唯一的state)
      if (Vue && _Vue === Vue) {
        if (process.env.NODE_ENV !== 'production') {
          console.error(
            '[vuex] already installed. Vue.use(Vuex) should be called only once.'
          )
        }
        return
      }
      // 若没有,则为这个Vue实例对象install一个唯一的Vuex
      Vue = _Vue
      // 将Vuex的初始化逻辑写进Vue的钩子函数里
      applyMixin(Vue)
    }
    

    上面便是 Vuex 源码中单例模式的实现办法了。通过这种方式,可以保证一个 Vue 实例(即一个 Vue 应用)只会被 install 一次 Vuex 插件,所以每个 Vue 实例只会拥有一个全局的 Store

    参考

    JavaScript设计模式与开发实践--曾探

    JavaScript惰性单例与vue性能优化应用

    JavaScript设计模式——单例模式

    若有错误或者对于vue对于单例模式更好的应用请在评论区留言,最后感谢阅读!!!

    设计模式是对语言不足的补充

    —— Peter Norvig


    起源地下载网 » 『设计模式』—— 单例模式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元