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

    正文概述 掘金(webInRun)   2021-05-14   460

    概念

    可以看出单例模式需满足两个条件

    • 只有一个实例
    • 可以全局访问

    模式类型

    "简单版本" 单例模式

    class SingleObject {
        constructor(name) {
            this.name = name;
            this.instance = null
        }
        getName() {
            return this.name
        }
        static getInstance(name) {
            // 判断是否已经 new 过1个实例
            if (!this.instance) {
                // 若这个唯一的实例不存在,则创建它并存储
                this.instance = new SingleObject(name)
            }
            // 如果这个唯一实例已经存在,则直接返回
            return this.instance
        }
    }
    
    const s1 = SingleObject.getInstance("张三")
    const s2 = SingleObject.getInstance("李四")
    console.info(s1.getName()) // 张三
    console.info(s2.getName()) // 张三
    console.info(s1 == s2) // true
    
    // 这里只能用 getInstance 才能保证单例,如果用 new SingleObject 就没办法保证了
    const s3 = new SingleObject("王五")
    console.info(s3.getName()) // 王五
    console.info(s3 == s2) // false
    

    在类里定义个静态方法 getIntance ,判断是否创建过实例,如果创建过则直接返回,没有则创建。 所以无论调用多少次 getIntance 返回的都是第一次创建的那个实例,自然 s1.getName 与 s2.getName 指向的 this 都是 s1

    而 s3 不是通过 getIntance 创建的,是直接 new 出来的,所以跟 s1 s2 都不一样

    缺点:

    • 这种单例模式存在两种创建实例方式,当采用 new 时就不再单例了
    • 判断是否实例化过(即单例判断)跟对象创建都放在 getIntance 处理,不符合单一职责原则

    闭包版单例模式

    const SingleObject = (function () {
        //在闭包里面定义了一个instance变量,将instance变量定义在外部会污染全局变量
        let instance = null
        return function (name) {
            // 判断变量是否为 null
            if (!instance) {
                this.name = name
                instance = this
            }
            return instance
        }
    })()
    SingleObject.prototype.getName = function () {
        return this.name
    }
    
    const s1 = new SingleObject("张三")
    const s2 = new SingleObject("李四")
    console.info(s1.getName()) // 张三
    console.info(s2.getName()) // 张三
    console.info(s1 == s2) // true
    
    const s3 = new SingleObject("王五")
    console.info(s3.getName()) // 张三
    console.info(s3 == s2) // true
    

    通过立即执行函数,以及闭包创建了单例函数,这下就又统一成以 new 的形式创建对象即可

    “代理版” 单例模式

    简单版本单例模式将单例管理与对象创建全整在一个类里,不符合单一职责原则,而代理版的作用就是将这两者分离

    class SingleObject {
        constructor(name) {
            this.name = name
        }
        getName() {
            return this.name
        }
    }
    const ProxySingleObject = (function () {
        let instance
        return function (name) {
            if (!instance) {
                instance = new SingleObject(name)
            }
            return instance
        }
    })()
    
    

    这样, SingleObject 只负责该类相关的属性跟方法, 至于管理单例就由 ProxySingleObject 处理,对外也只暴露 ProxySingleObject 即可。可以配合模块化,只 export ProxySingleObject ,具体见代码

    测试:

    import ProxySingleObject from './代理单例模式'
    const s1 = new ProxySingleObject("张三")
    const s2 = new ProxySingleObject("李四")
    console.info(s1.getName()) // 张三
    console.info(s2.getName()) // 张三
    console.info(s1 == s2) // true
    
    const s3 = new ProxySingleObject("王五")
    console.info(s3.getName()) // 张三
    console.info(s3 == s2) // true
    

    惰性单例模式

    利用自执行函数在代码执行时就把对象实例创建,这种有些不妥,像是弹出登录框,有可能根本就不需要登录即可访问首页。

    这时我们并不需要在页面加载时就去创建一个弹窗。我们大可在需要用的时候才去创建

    现在就让我们实现下登录弹框

    <style>
        #modal {
            height: 200px;
            width: 200px;
            line-height: 200px;
            position: fixed;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            border: 1px solid black;
            text-align: center;
        }
    </style>
    
    <body>
        <button id='login'>登录</button>
    </body>
    <script>
        var createLoginModal = (function () {
            let modal = null
            return function () {
                if (!modal) {
                    modal = document.createElement('div')
                    modal.innerHTML = "登录框"
                    modal.id = "modal"
                    modal.style.display = 'none'
                    document.body.appendChild(modal)
                }
                return modal
            }
        })()
        document.getElementById("login").onclick = function () {
            var modal = createLoginModal()
            modal.style.display = 'block'
        }
    </script>
    

    通用惰性单例模式

    上面惰性单例模式,也是存在单例控制以及创建融合在一起的问题,另外也不够通用。何为通用呢? 就是将单例控制抽离成单独的函数,控制保证只有一个对象,类似这样的:

    var obj 
    if (!obj) {
        obj = xxx
    }
    

    将代码修改下:

    把这个操作的逻辑封装到一个 getSingle 函数中,然后把要执行的函数当作参数传入进去:

    function getSingle(fn) {
    	let result 
    	return function() {
    		result || (result = fn.apply(this,argumments))
    	}
    }
    

    这样我们上面写的创建弹窗的方法就可以完全抽离出来:

    var createLoginModal = function () {
         var modal = document.createElement('div')
         modal.innerHTML = "登录框"
         modal.id = "modal"
         modal.style.display = 'none'
         document.body.appendChild(modal)
         return modal
    }
    var createSingleLoginModal = getSingle(createLoginModal)
    
    document.getElementById('login').onclick = function() {
        var modal = createSingleLoginModal()
        modal.style.display = 'block'
    }
    

    这样,后续再有其他要实现单例的,只要将函数作为参数传入即可,比如购物车,都可以调用 getSingleton(...) 生成对应实例对象的方法

    单例模式使用场景

    引用第三方库(多次引用只会使用一个库引用,如 jQuery)

    //简单模拟JQuery中为实现单一$,所做的单例模式判断
    if(!window.Jquery){
        return window.Jquery()
    }else{
        //其他代码
    }
    

    全局态管理 store (Vuex / Redux)

    let Vue // 这个Vue的作用和上面的instance作用一样
    ...
    
    export function install (_Vue) {
      // 判断传入的Vue实例对象是否已经被install过Vuex插件(是否有了唯一的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)
    }
    

    参考链接

    JavaScript 设计模式核⼼原理与应⽤实践

    结语

    你的点赞是对我最大的肯定,如果觉得有帮助,请留下你的赞赏,谢谢!!!


    起源地下载网 » javascript 设计模式之单例模式

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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