理解模块化的javascript模式

时间:2012-07-24 14:45:14

标签: javascript jquery design-patterns modular

我正在尝试写'更好'的javascript。

以下是我发现的一种模式,我正在尝试采用。但是,我对它的使用感到有些困惑。

比如说,我有一个名为“Jobs”的页面。该页面上的任何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对象里面的返回对象里面?在init()函数内部?

我只是试图了解一个稳定的,基本的框架,用于放置简单的功能。我不是在构建JS应用程序,我只是想编写一个比它更强大和可维护的代码。目前。热烈欢迎任何和所有建议:)

4 个答案:

答案 0 :(得分:2)

您可以按照自己喜欢的modules / objects来分解应用程序。

例如,您可以拥有另一个对象/模块来缓存和定义所有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()还是诸如此类。它需要符合您的要求。

但你应该尝试创建与necesarry一样多的特定模块/对象。当然,如果将这些单例/模块/对象分成多个javascript文件并按需加载它们并且在你说刀之前更有意义,那么你就处于模块化编程模式的世界中,处理requireJS和AMD或CommonJS模块。

答案 1 :(得分:1)

封装方面,你没关系:你甚至可以在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);
       }
   }; 

答案 2 :(得分:1)

你现在正在这样做的方式也正如我所做的那样,我通常在匿名函数本身内创建窗口对象然后在其中声明(在这种情况下:jClass = window.jClass)。

(function (jClass, $, undefined) { 

    /// <param name="$" type="jQuery" />

    var VERSION      = '1.31';
    UPDATED_DATE = '7/20/2012';

    // Private Namespace Variables
    var _self         = jClass; // internal self-reference
    jClass        = window.jClass; // (fix for intellisense)
    $             = jQuery; // save rights to jQuery (also fixes vsdoc Intellisense)

    // I init my namespace from inside itself
    $(function () {
        jClass.init('branchName');
    });

    jClass.init = function(branch) {
      this._branch = branch;
      this._globalFunctionality({ globalDatePicker: true });
      this._jQueryValidateAdditions();

      //put GLOBAL IMAGES to preload in the array
      this._preloadImages( [''] );

      this._log('*******************************************************');
      this._log('jClass Loaded Successfully :: v' + VERSION + ' :: Last Updated: ' + UPDATED_DATE);
      this._log('*******************************************************\n');
};

    jClass._log = function() {
    //NOTE: Global Log (cross browser Console.log - for Testing purposes)
    //ENDNOTE 

    try { console.log.apply(console, arguments); }  
    catch (e) {
        try { opera.postError.apply(opera, arguments); }
        catch (e) { /* IE Currently shut OFF : alert(Array.prototype.join.call(arguments, ' '));*/ }
    }   
};

}(window.jClass= window.jClass|| {}, jQuery));

我这样完全匿名的原因是,让我们在另一个文件中说我想为这个jClass添加更多功能。我只想创建另一个:

(function jClass, $, undefined) {

jClass.newFunction = function (params) {
    // new stuff here
};

}(window.jClass = window.jClass || {}, jQuery))

正如你所看到的,我更喜欢object.object表示法,但你可以使用对象文字对象:对象,这取决于你!

通过将所有这些分开,并且没有实际页面逻辑进行封装,可以更容易地在globalJS文件中使用它,并且站点上的每个页面都可以使用它。如下例。

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

您不希望将模型逻辑与业务逻辑交织在一起,因此您在正确的路径上将两者分开,并允许您的全局命名空间/类/等更灵活!

答案 3 :(得分:0)

您可以在此处找到有关模块模式的全面研究:http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html它涵盖了块范围模块方法的所有方面。但是在实践中你会有很多文件封装你的代码,所以问题是如何将它们组合起来。 AMD ...每个模块加载产生的多个HTTP请求都会损害您的页面响应时间。因此,您可以将CommonJS编译为适合浏览器使用的单个JavaScript文件。看看它是多么容易http://dsheiko.github.io/cjsc/