JavaScript模块模式与子模块交叉访问或更好的模式?

时间:2014-06-06 14:44:55

标签: iife javascript

也许这是我的问题的错误方法,但这就是我在这里的原因。在下面的代码中是带有子模块的JavaScript模块模式的示例。当我构建这个时,我意识到一些子模块需要"调用"彼此的方法。

我知道使用完整的呼叫admin.subModuleB.publicB_2();是错误的,但这是因为IIFE功能无法调用"他们自己"直到实例化,例如"模块"在主命名空间等中不可用...

我的想法是这种模式对我的情况不正确。模块封装的目的是保持私密性,除非被暴露。那么什么是更好的模式?

var module = (function($, window, document, undefined) {

    return {
        subModuleA : (function() {
            var privateA = 100;
            return {
                // We have access to privateA
                publicA_1 : function() {
                    console.log(privateA);
                    // How do I use a method from publicB_1
                    // the only way is:
                    module.subModuleB.publicB_2();
                    // but I don't want to use "module"
                },
                publicA_2 : function() {
                    console.log(privateA);
                }
            }
        })(),
        subModuleB : (function() {
            var privateB = 250;
            return {
                // We have access to privateB
                publicB_1 : function() {
                    console.log(privateB);
                },
                publicB_2 : function() {
                    console.log(privateB);
                    // I have access to publicB_1
                    this.publicB_1();
                }
            }
        })()
    }

})(jQuery, window, document);

1 个答案:

答案 0 :(得分:3)

您实际拥有的是依赖项问题。子模块A依赖于子模块B.有两种解决方案可以想到。

  1. 在函数闭包中将两个模块定义为自己的变量,但在一个对象中将它们一起返回。

  2. 您真正想要的是可实例化的类,其中A类依赖于B类。

  3. 由于解决方案#1最接近您当前的代码,因此我们先探讨一下。

    在闭包内单独定义两个模块

    var module = (function($, window, document, undefined) {
    
        var SubModuleA = function() {
            var privateA = 100;
    
            return {
                // We have access to privateA
                publicA_1 : function() {
                    console.log(privateA);
                    // Refer to SubModuleB via the private reference inside your "namespace"
                    SubModuleB.publicB_2();
                    // but I don't want to use "module"
                },
                publicA_2 : function() {
                    console.log(privateA);
                }
            };
        }();
    
        var SubModuleB = function() {
            var privateB = 250;
    
            return {
                // We have access to privateB
                publicB_1 : function() {
                    console.log(privateB);
                },
                publicB_2 : function() {
                    console.log(privateB);
                    // I have access to publicB_1
                    this.publicB_1();
                }
            };
        }();
    
        // Return your module with two sub modules
        return {
            subModuleA : SubModuleA,
            subModuleB : SubModuleB
        };
    
    })(jQuery, window, document);
    

    这允许您使用局部变量将两个子模块引用到模块的闭包(SubModuleASubModuleB)。全局上下文仍然可以将它们称为module.subModuleAmodule.subModuleB

    如果子模块A使用子模块B,则需要解决子模块B是否需要向全局环境显示的问题。

    老实说,这个 打破了封装,因为子模块A中并不存在子模块A的所有功能。事实上,如果没有子模块B,子模块A将无法正常运行。

    根据您的具体情况,模块模式似乎是一种反模式,也就是说,您正在使用错误的工具来完成工作。实际上,您有两个相互依赖的对象分类。我认为你需要"课程" (JavaScript构造函数)和传统的OOP实践。

    使用JavaScript构造函数("类")

    首先,让我们重构你的"模块"分为两类:

    var module = (function($, window, document, undefined) {
    
        function ClassA(objectB) {
            var privateA = 100;
    
            this.publicA_1 = function() {
                console.log(privateA);
                objectB.publicB_2();
            };
    
            this.publicA_2 = function() {
                console.log(privateA);
            };
        }
    
        function ClassB() {
            var privateB = 250;
    
            this.publicB_1 = function() {
                console.log(privateB);
            };
    
            this.publicB_2 = function() {
                console.log(privateB);
                this.publicB_1();
            };
        }
    
        // Return your module with two "classes"
        return {
            ClassA: ClassA,
            ClassB: ClassB
        };
    
    })(jQuery, window, document);
    

    现在,为了使用这些类,您需要一些代码来从构造函数生成对象:

    var objectA = new module.ClassA(new module.ClassB());
    objectA.publicA_1();
    objectA.publicA_2();
    

    这最大化了代码重用,并且因为您将module.ClassB的实例传递给module.ClassA的构造函数,所以您将这些类彼此分离。如果您不希望外部代码管理依赖项,您可以随时调整ClassA

    function ClassA() {
        var privateA = 100,
            objectB = new ClassB();
    
        this.publicA_1 = function() {
            console.log(privateA);
            objectB.publicB_2();
        };
    
        this.publicA_2 = function() {
            console.log(privateA);
        };
    }
    

    现在,您可以使用函数闭包中的名称来引用module.ClassBClassB。这里的优点是外部代码不必为module.ClassA提供所有依赖项,但缺点是您仍然有ClassAClassB相互耦合。

    同样,这引出了全球背景是否需要向ClassB透露的问题。