设计模式|JavaScript实现单例模式(下篇)

这是我参与更文挑战的第22天,活动详情查看:更文挑战

接上篇

设计模式|JavaScript实现单例模式(上篇)

juejin.cn/post/697704…

JavaScipt中的单例模式

对于传统面向对象语言而言,单例对象是从”类”中创建而来的,比如Java中,如果需要某个对象,就必须先定义一个类,对象从类中被创建出来。

但是对于JavaScript而言,其实是一门无类(class-free)语言,因此在JavaScript中创建对象的方法特别简单,只需要保证单例模式的核心——确保只有一个实例,并提供全局访问。

那么他就是一个单例类。

直接在全局上下文定义一个对象字面量的全局变量,也可以把它看做单例类,但是全局变量很容易造成命名空间污染的问题。

// 全局上下文的a对象,它也可以看做是一个单例类。
let a = {};
复制代码

模拟命名空间,可以减少全局变量造成污染的概率。

/**
* 对象字面量的方法模拟命名空间
*/
let namespace1 = {
    a: function(){
        console.log(1);
    },
    b: function(){
        console.log(2);
    }
};
/**
* 动态创建命名空间
*/
let MyApp = {};

MyApp.namespace = function( name ){
    var parts = name.split( '.' );
    var current = MyApp;
    for ( var i in parts ){
        if ( !current[ parts[ i ] ] ){
            current[ parts[ i ] ] = {};
        }
        current = current[ parts[ i ] ];
    }
};

MyApp.namespace( 'event' );
MyApp.namespace( 'dom.style' );

console.dir( MyApp );

// 上述代码等价于:

 let MyApp = {
     event: {},
     dom: {
         style: {}
     }
 };

/**
* 使用闭包封装私有变量
*/
let user = (function(){
    let __name = 'sven',
        __age = 29;

    return {
        getUserInfo: function(){
            return __name + '-' + __age;
        }
    }

})();
复制代码

在ES6中,我们可以模块(module)来模拟命名空间。

  1. 创建一个模块文件
// 模块文件
// MyApp.js
let MyApp = {
     event: {},
     dom: {
         style: {}
     }
 };

export default MyApp;
复制代码
  1. 页面中引入模块
<script type="module" src="./MyApp.js"></script>
复制代码
  1. 需要使用该模块的js文件中导入模块并使用
import MyApp from './MyApp.js';

console.log(MyApp)
复制代码

懒汉式和饿汉式

单例模式的写法有很多,懒汉式,饿汉式,注册式,序列化式等等……

这里着重对懒汉式单例和饿汉式单例做一个比较讲解。

  • 懒汉式:默认不会实例化,什么时候用到了,才会创建对象实例。
  • 饿汉式:类加载的时候就会进行实例化,并且创建单例对象实例。

像先前的例子,Singleton.getInstance在调用的时候才会被创建,这种就是典型的懒汉式单例。

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

用在实际的例子,比如创建一个登录弹框,我们在JavaScript也可以这样做。

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';
};

复制代码

通用的懒汉式单例

上面的登录弹框写法虽然可用,但是违反了设计模式的”单一职责原则”,创建对象和管理单例的逻辑都耦合在了一起。如果之后要创建更多的单例,就要重复复制粘贴一遍又一遍单例代码。

所以我们需要把管理单例的逻辑从原来的代码中抽离出来。

let getSingle = function( fn ){
    let result; // 保存在闭包中,不会被销毁。
    return function(){
        return result || ( result = fn.apply(this, arguments ) );
    }
};
复制代码

接着,无论有多少种不同的创建实例对象的方法,getSingle这个管理管理方法都能很好的完成任务。

/**
* 例子1:创建登录弹窗,保证全局只有一个登录弹窗
*/
// 创建登录弹窗的方法
let createLoginLayer = function(){
    var loginDiv = document.createElement( 'div' );
    loginDiv.innerHTML = '我是登录浮窗';
    loginDiv.style.display = 'none';
    document.body.appendChild( loginDiv );
    return loginDiv;
};
// 获取创建登录弹窗方法的单例
var createSingleLoginLayer = getSingle( createLoginLayer );

document.getElementById( 'loginBtn' ).onclick = function(){
    var loginLayer = createSingleLoginLayer(); // 使用这个单例
    loginLayer.style.display = 'block';
};
/**
** 例子2:创建iframe,保证全局只有一个iframe
*/
// 创建iframe的单例方法
let createSingleIframe = getSingle( function(){
    let iframe = document.createElement ('iframe');
    document.body.appendChild(iframe);
    return iframe;
});

document.getElementById( 'loginBtn' ).onclick = function(){
    var loginLayer = createSingleIframe(); // 使用这个单例
    loginLayer.src = 'http://baidu.com';
};
/**
** 例子3:给列表绑定click事件,保证列表只在初始化后执行一次事件绑定
*/
// 绑定点击事件的方法
let bindEvent = getSingle(function(){
    document.getElementById( 'div1' ).onclick = function(){
        console.log ( 'click' );
    }
    return true;
});

var render = function(){
    console.log( '开始渲染列表' );
    bindEvent();
};

// 虽然执行了3次render,但是绑定事件的操作只执行了一次。
render();
render();
render();

复制代码

参考资料

《JavaScript设计模式与开发实践》——单例模式

www.ituring.com.cn/book/1632

Proxy代理实现单例模式

juejin.cn/post/684490…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享