对象文字或模块化Javascript设计模式

时间:2013-03-30 12:55:52

标签: javascript jquery

这可能已经被问过很多次了,我一直在搜索SO,但到目前为止我读到的所有答案都不是我正在寻找的。

我正在开发一个网站,其中包含显示/隐藏的中等DOM元素,一些AJAX调用,以及其他可能的东西。所以我将有两个主要的脚本文件(HTML5 Boilerplate标准)

plugins.js // third party plugins here
site.js // all my site specific code here

以前我使用的是对象文字设计模式,所以我的site.js是这样的:

var site = {
  version: '0.1',
  init: function() {
    site.registerEvents();
  },
  registerEvents: function() {
    $('.back-to-top').on('click', site.scrollToTop);
  },
  scrollToTop: function() {
    $('body').animate({scrollTop: 0}, 400);
  }
};

$(function() {
  site.init();
});

到目前为止一切都很好,它的可读性很好,所有方法都是公开的(我有点像这样,因为我可以在必要时通过Chrome Dev Tools直接测试它们)。但是,我打算将网站的某些功能分解为更加模块化的风格,所以我希望在上面的代码(或单独的文件)中有类似的东西:

site.auth = {
  init: function() {
    site.auth.doms.loginButton.on('click', site.auth.events.onLoginButtonClicked);
  },
  doms: {
    loginButton: $('.login'),
    registerButton: $('.register')
  },
  events: {
    onLoginButtonClicked: function() {
    }
  },
  fbLogin: function() {
  }
};

site.dashboard = {
};

site.quiz = {
};

// more modules

如您所见,它非常易读。然而,有一个明显的缺点,就是我必须编写像site.auth.doms.loginButtonsite.auth.events.onLoginButtonClicked这样的代码。突然间,它变得难以阅读,只有功能越复杂,它才会越长。然后我尝试了模块化模式:

var site = (function() {
  function init() {
    $('.back-to-top').on('click', scrollToTop);
    site.auth.init();
  }

  function scrollToTop() {
    $('body').animate({scrollTop: 0}, 400);
  }

  return {
    init: init
  }
})();

site.auth = (function() {
  var doms = {
    loginButton: $('.login'),
    registerButton: $('.register')
  };

  function init() {
    doms.loginButton.on('click', onLoginButtonClicked);
  }

  function onLoginButtonClicked() {

  }

  return {
    init: init
  }
})();

// more modules

正如你所看到的,那些长名称已经消失了,但是我想我必须在site.init()函数中初始化所有其他模块来构建它们吗?然后我必须记住返回其他模块需要访问的功能。两者都没关系我想虽然有点麻烦,但总的来说,我是否采用了模块化模式的更好的工作流程?

1 个答案:

答案 0 :(得分:15)

正确的答案当然是:“这取决于”。

如果您对所有数据和所有方法完全没问题,那么对于您网站的每个部分都是100%公开的,那么只需使用单个文字(或多个文字),如果需要,可以使用嵌套对象,这是完全正确的,假设你可以防止它变成一个庞大的代码球。

如果你想要任何类型的私有状态,它具有任何类型的持久性(即:每次运行函数时都不会重置),那么揭示模块就很棒。

那说:
根本没有透露模块的要求,你可以使用.init方法 如果您的模块可以是自包含的,那么只需专注于导出您想要公开的内容。

为此,当我编写团队可能会查看的代码时,我发现自己正在创建一个public_interface对象并返回它(您返回的匿名对象的命名版本)。

这样做的好处很小,除了添加一些理解,即需要公开的任何内容都需要附加到界面上。

您目前使用它的方式:

var module = (function () { /* ... */ return {}; }());

module.submodule = (function () { /*...*/ return {}; }());

不比文字更好或更差,因为你可以轻松地做到这一点:

var module = {
    a : "",
    method : function () {},
    meta : { }
};

module.submodule = {
    a : "",
    method : function () {},
    meta : { }
};

在你找到一些对你不起作用的东西之前,先处理满足你需求的东西。

就个人而言,我通常会将任何仅数据对象构建为文字:config-objects,来自其他连接的对象等...

任何需要一两个方法的简单污垢对象,并且可以通过仅嵌套一个或两个级别来构建,我也可以按字面构建(只要不需要初始化)。

// ex:
var rectangle = {
    width  : 12,
    height : 24,
    area : 0,
    perimeter : 0,
    init_area : function () { this.area = this.width * this.height; return this; }, // buh...
    init_perimeter : function () { this.perimeter = (this.width * 2) + (this.height * 2); return this; } // double-buh...
}.init_area().init_perimeter();

如果我需要其中的几个,也许我会做一个构造函数 但是,如果我只需要这样一个独特的东西,那么就不会让我有些头疼,只做这样的事情:

var rectangle = (function (width, height) {
    var public_interface = {
        width  : width,
        height : height,
        area   : width * height,
        perimeter : (2 * width) + (2 * height)
    };
    return public_interface;
}(12, 24));

如果需要更高级的计算,我可以将任何额外的变量保密,并从内部处理它们。
如果我需要在对象中包含敏感数据,并且需要处理该数据的函数,那么我可以使用公共函数来调用这些私有函数,并返回结果,而不是提供访问。

此外,如果我重构我的代码,并决定在某个时候重命名rectangle,那么任何涉及rectangle的嵌套3或更深的函数都必须进行修改,以及。
同样,如果您正在构建方法,以便他们不需要直接询问任何超过this的对象,那么您将不会遇到此问题...

...但如果您的界面如下所示:

MyApp.myServices.webService.send();

并期待找到:

MyApp.appData.user.tokens.latest;  // where personally, I might leave tokens in a closure

如果您更改appData模块的结构,您将在webService模块中遇到各种错误,直到您找到对旧格式的每个引用,并将它们全部重命名。