理解模块化javascript模式

理解模块化javascript模式,javascript,jquery,design-patterns,modular,Javascript,Jquery,Design Patterns,Modular,我正在尝试编写“更好”的javascript 下面是我发现的一种模式,我正在尝试采用。然而,我对它的使用有点困惑 比如说,我有一个叫做“工作”的页面。该页面上的任何JS功能都将封装在如下内容中: window.jobs = (function(jobs, $, undefined){ return { addNew: function(){ // job-adding code } } })(window.jobs|| {}

我正在尝试编写“更好”的javascript

下面是我发现的一种模式,我正在尝试采用。然而,我对它的使用有点困惑

比如说,我有一个叫做“工作”的页面。该页面上的任何JS功能都将封装在如下内容中:

window.jobs = (function(jobs, $, undefined){
    return {
        addNew: function(){
            // job-adding code
        }
    }
})(window.jobs|| {}, jQuery);

$(function(){
    $('.add_job').on('click', function(event){
        event.preventDefault();
        window.jobs.addNew();
    });
});
正如您可能推断的那样,我所做的只是将匿名事件处理程序函数中的所有代码替换为对全局作业对象中的函数的调用。我不知道为什么这是一件好事,除了它减少了可变碰撞的可能性,使整个事情变得更整洁,但这对我来说已经足够好了

一个可能相当明显的问题是:我所有的事件绑定init类型的东西仍然放在我闪亮的新jobs对象之外:它应该在哪里?在jobs对象内部?在返回对象内部在作业对象内部?在init()函数中


我只是想了解一个稳定的、基本的框架,用以实现简单的功能。我不是在构建JS应用程序,我只是想编写比当前更健壮、更可维护的代码。欢迎所有建议:)

封装方面,您很好:您甚至可以在jQuery闭包中声明
addNew
,这样您仍然可以避免全局范围。我认为你所得到的更多的是实现一些接近MVC架构的东西

我喜欢做的事情是创建一个用DOM元素实例化的对象,该对象负责自己的绑定/提供访问其控件的方法等

例如:

   // (pretend we're inside a closure already)
   var myObj = function(args){
       this.el = args.el; // just a selector, e.g. #myId
       this.html = args.html;
       this.bindings = args.bindings || {};
   }

   myObj.prototype.appendTo = function(elem){
       elem.innerHTML += this.html;
       this.bindControls();
   };

   myObj.prototype.remove = function(){
       $(this.el).remove(); // using jQuery
   };

   myObj.prototype.bindControls = function(){
       for(var i in this.bindings){ // event#selector : function
           var boundFunc = function(e){ return this.bindings[i].call(this,e); };
           $(this.el).on(i,boundFunc);
       }
   }; 

您现在的操作方式正是我的操作方式,我通常在匿名函数本身中创建窗口对象,然后在其中声明(在本例中:jClass=window.jClass)

正如您所看到的,我更喜欢object.object符号,但是您可以使用object-literals-object:object,这取决于您

无论哪种方式,将所有这些内容分开封装,而不使用实际的页面逻辑,都会使globalJS文件中包含这些内容变得更容易,并且站点上的每个页面都可以使用它们。例如下面的例子

jClass._log('log this text for me');

您不想将模型逻辑与业务逻辑纠缠在一起,因此您的应用程序将二者分开,并允许您的全局名称空间/类/etc更加灵活

您也可以将应用程序分解为任意数量的
模块
/
对象

例如,您可以有另一个对象/模块来缓存和定义所有DOM节点,另一个对象/模块只处理任何事件。例如:

(function ( win, doc, $, undef ) {
    win.myApp = win.myApp || { };

    var eventHandler = {
        onJobClick: function( event ) {
            event.preventDefault();
            myApp.addNew();
        }
    };

    var nodes = (function() {
        var rootNode = $( '.myRootNode' ),
            addJob = rootNode.find( '.add_job' );

        return {
            rootNode: rootNode,
            addJob: addJob
        };
    }());

    $(function() {
        myApp.nodes.addJob.on( 'click', myApp.handler.onJobClick );
    });

    myApp.nodes = nodes;
    myApp.handler = eventHandler;
}( this, this.document, jQuery ));
在这个(模块)模式中如何创建单例并不重要,无论是作为文本、构造函数、
Object.create()
还是其他什么。它需要符合您的要求


但是,您应该尝试创建尽可能多的特定模块/对象。当然,如果将这些单例/模块/对象分离成多个javascript文件并按需加载更有意义,那么在你说刀子之前,你已经进入了模块化编程模式的世界,处理
requireJS
和AMD或CommonJS模块。

您可以在这里找到对模块模式的全面研究:它涵盖了块范围模块方法的所有方面。然而,在实践中,会有相当多的文件封装代码,所以问题是如何将它们结合起来。AMD。。。每个模块加载产生的多个HTTP请求会损害页面响应时间。因此,您可以将CommonJS编译成一个适合在浏览器中使用的JavaScript文件。看看它有多简单

这听起来很像jQuery插件模式,这是否准确?在任何情况下,看到它像那样崩溃都是非常有用的。谢谢你——在这里,请进行投票:)非常感谢你提供的非常有用的答案;我知道那里有大量的信息,从Crockfords、Osmanis和Resigs,但是结合您自己的代码来解释这些概念是非常有帮助的。我将把jAndy的回答标记为已接受,因为尽管我显然无法说明哪一个是“正确”的解决方案,但他的回答似乎最好地解释了我所采取的方法应该如何应用。其思想是保持模块(JavaScript文件)作用域的隔离。但我们仍然需要模块之间的通信。这可以很容易地通过普通方式实现-通过
exports
从模块导出您想要的任何内容,并通过
require
在模块中导入。您可以将CommonJs模块编译为一个文件,该文件适合使用或的浏览器。在ES6中,它甚至以更明确的方式实现——还有另一种方式。您可以定义注入每个模块作用域的中介对象
jClass._log('log this text for me');
(function ( win, doc, $, undef ) {
    win.myApp = win.myApp || { };

    var eventHandler = {
        onJobClick: function( event ) {
            event.preventDefault();
            myApp.addNew();
        }
    };

    var nodes = (function() {
        var rootNode = $( '.myRootNode' ),
            addJob = rootNode.find( '.add_job' );

        return {
            rootNode: rootNode,
            addJob: addJob
        };
    }());

    $(function() {
        myApp.nodes.addJob.on( 'click', myApp.handler.onJobClick );
    });

    myApp.nodes = nodes;
    myApp.handler = eventHandler;
}( this, this.document, jQuery ));