通过对象字面量的单例与模块模式的对比,代码组织的利弊

Singleton through object literal versus module pattern, code organization pros&cons

本文关键字:代码 模式 模块 对象 单例      更新时间:2023-09-26

你更喜欢哪一个?一个优于另一个?就我个人而言,我发现singleton很难阅读和维护,很多上下文变化(this),总是以通过var self = this缓存正确的上下文结束(或像$.proxy这样的代理)+我总是有一个全局变量(singleton),而在第二种方法中,如果模块不需要暴露任何API,我可以避免创建全局变量。

简单的例子:
(用户点击"添加到购物车",一些ajax被触发,并在成功创建html(通知))

<div class="cart">
    <a href="#" data-product-id="200"> Add to cart </a>
</div>
<

模块模式/strong>
不是真正的模块模式的定义,因为我没有返回任何从它,点击处理程序可以通过闭包访问外部作用域。

;(function(window, $, undefined) {
    function constructHtml(data) {...}
    function addToCart(product_id, quantity) {
        ...
        $.ajax({
            ...
            success: function(data) { constructHtml(data); }
        });
    }

    $(function() {
        var $addBtn = $('div.cart a');
        var productId = $addBtn.data('product-id');
        $addBtn.click(function(e) {
            e.preventDefault();
            addToCart(productId);
        });
    });
})(this, jQuery);

Singleton - Object literal(同样的例子)

var Cart = {
    settings: {
        $addBtn: $('div.cart a'),
        productId: $('div.cart a').data('product-id') 
    },
    init: function() {
        this.bindUiActions();
    },
    bindUiActions: function() {
        var self = this;
        this.settings.$addBtn.click(function(e) {
            e.preventDefault();
            self.addToCart(self.settings.productId);
        });
    },
    addToCart: function(product_id, quantity) {
        ...
        var self = this;
        $.ajax({
            ...
            success: function(data) {
                self.constructHtml(data);
            }
        });
    },
    constructHtml: function(data) {...}
};
Cart.init();

虽然单例与模块模式可以是一个合法的问题,与你给的例子,这个问题应该被改写为单例与IIFE,因为它不是模块模式本身,而是IIFE,模块模式被包裹在这是保持cruft出你的Cart单例。即便如此,这也不是一个vs类型的问题——IIFE和Singleton都可以一起使用。IIFE的目的只是将一些东西排除在全局作用域之外。

所以…,您可以将您的单例代码打包到IIFE中,如下所示

;(function(){
  var Cart = {
    settings: {
      // Your code ...
  };
  Cart.init();
})();

在这种情况下,您将代码编写为Singleton,但将其封装在IIFE中,并且没有使用全局作用域。IIFE完全独立于您选择如何编写和组织代码。对于单例模式与模块模式的问题,您需要一个不同的示例。

IIFE(立即调用的函数表达式):

(function() {
    console.log('run this in your browser console')
})()

模块模式:

var module = (function () {
    var privateScopeVar = 1 // scope of the IIF  
    // the 'this' context in this scope is the global object (e.g. window)
    // returns literal object who's method's scopes are lexically bound 
    // to the IIF scope
    return {
      publicMethod: () => privateScopeVar // publicMethod scope can access IIF scope 
    }
})()

Singleton Literal +在EcmaScrip5中包含/导入多次后不能被覆盖的Singleton Literal:

var singleInstance = { firstName: 'Greg' }
var singleInstance = singleInstance || { firstName: 'Greg' }

在EcmaScrip6中不能重写或初始化多次的单例字面量:

const singleInstance = { 
    firstName: 'Greg', 
    called: false, 
    init: () => { 
        if (!this.called) {
            console.log('only once')
            this.called = true
        }
    } 
}
// of course singleInstance.called = false; singleInstance.init() is always possible

单例IIF(提供私有状态)

const singleton = (function () {
    // IIF private scope ensures there is a single instance ever
    var storedInstance
    // Also some private state for the singleton instance as well
    const firstName = 'Greg'

    // literal object that holds constructor function
    const Singleton = {
        getInstance: () => {
            if (!storedInstance) {
                storedInstance= {
                    getName: () => firstName
                }
            }
            return storedInstance
        }
    }
    return Singleton
})()