12

This may have already been asked lots of times, and I've searched around SO but so far all the answers I read aren't exactly what I'm looking for.

I'm working on a website with moderate DOM elements showing/hiding, some AJAX calls, and probably something else. So I'll be having two main script files (HTML5 Boilerplate standard)

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

Previously I'm using object literal design pattern, so my site.js is something like this:

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();
});

So far so good, it's nicely readable, all methods are public (I kinda like this, as I can test them via Chrome Dev Tools directly if necessary). However, I intend to decouple some of the site's functionality into more modular style, so I want to have something like this below the code above (or in separate files):

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

As you can see, it is very readable. However there is one obvious downside, which is I have to write code like site.auth.doms.loginButton and site.auth.events.onLoginButtonClicked. Suddenly it becomes hard to read, and it will only grow longer the more complex the functionality gets. Then I tried the modular pattern:

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

As you can see, those long names are gone, but then I guess I have to init all other modules in the site.init() function to construct them all? Then I have to remember to return the functions that need to be accessible by other modules. Both of them are okay I guess albeit a bit of a hassle, but overall, am I onto a better workflow working with modular pattern?

4

1 回答 1

16

当然,这里的正确答案是:“视情况而定”。

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

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

也就是说:
你有一个.init方法根本不是揭示模块的要求。
如果您的模块可以是独立的,那么只需专注于导出您想要公开的内容。

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

这样做的好处是微乎其微的,只是增加了对需要公开的任何内容都需要附加到接口的理解。

您当前使用它的方式:

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

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

没有比文字更好或更差,因为您可以很容易地做到这一点:

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

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

直到你遇到对你不起作用的东西,才能满足你的需求。

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

任何可能需要一种或两种方法的简单对象,并且可以通过仅嵌套一层或两层来构建,我也可以按字面意思构建(只要它不需要初始化)。

// 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在某个时候重命名,那么任何嵌套 3 层或更深的函数(所引用的)rectangle也必须进行修改。
同样,如果您正在构建您的方法,以便它们不需要直接请求任何比 更远的对象this,那么您将不会遇到这个问题......

...但如果你有一个看起来像这样的界面:

MyApp.myServices.webService.send();

它期待找到:

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

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

于 2013-03-30T14:37:13.797 回答