4

我正在尝试编写“更好的”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() 函数内部?

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

4

4 回答 4

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()还是诸如此类的东西。它需要符合您的要求。

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

于 2012-07-24T15:02:28.783 回答
1

封装方面,你很好:你甚至可以addNew在 jQuery 闭包中声明,你仍然可以避免全局范围。我认为你得到的更多的是实现接近 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);
       }
   }; 
于 2012-07-24T15:00:28.563 回答
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 表示法,但您可以使用对象文字 object : object,这取决于您!

无论哪种方式,通过将所有这些分开,并在没有实际页面逻辑的情况下进行封装,可以更轻松地将其包含在 globalJS 文件中,并且您网站上的每个页面都可以使用它。比如下面的例子。

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

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

于 2012-07-24T15:02:21.890 回答
0

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

于 2014-08-05T11:48:09.667 回答