简体   繁体   English

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

[英]Object Literal or Modular Javascript Design Pattern

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. 我正在开发一个网站,其中包含显示/隐藏的中等DOM元素,一些AJAX调用,以及其他可能的东西。 So I'll be having two main script files (HTML5 Boilerplate standard) 所以我将有两个主要的脚本文件(HTML5 Boilerplate标准)

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: 以前我使用的是对象文字设计模式,所以我的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();
});

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). 到目前为止这么好,它的可读性很好,所有方法都是公开的(我有点像这样,因为我可以直接通过Chrome Dev Tools测试它们)。 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 . 但是有一个明显的缺点,就是我必须编写像site.auth.doms.loginButtonsite.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? 正如你所看到的,那些长名称已经消失了,但是我想我必须在site.init()函数中初始化所有其他模块来构建它们吗? 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? 他们两个都没关系我想虽然有点麻烦,但总的来说,我是否采用了模块化模式的更好的工作流程?

The correct answer, here, of course, is: "it depends". 当然,正确的答案是:“它取决于”。

If you're totally okay with all data and all methods, for every section of your site being 100% public, then just using a single literal (or multiple literals), with nested objects if desired, is totally fine, assuming that you can keep it from turning into one gigantic ball of code. 如果您对所有数据和所有方法完全没问题,那么对于您网站的每个部分都是100%公开的,那么只要使用单个文字(或多个文字),如果需要,可以使用嵌套对象,假设您可以防止它变成一个巨大的代码球。

If you want any kind of private state, which has any kind of persistence (ie: doesn't reset every time you run a function), then the revealing-module is great. 如果你想要任何类型的私有状态,它具有任何类型的持久性(即:每次运行函数时都不会重置),那么揭示模块就很棒了。

That said: 那说:
It's not a requirement of the revealing-module for you to have an .init method, at all. 根本不需要具有.init方法的揭示模块。
If your module can be self-contained, then just focus on exporting what you WOULD like to make public. 如果您的模块可以是自包含的,那么只需专注于导出您想要公开的内容。

To this end, when I'm writing code which a team might look at, later, I'm finding myself creating a public_interface object and returning it (the named version of the anonymous object you return). 为此,当我编写团队可能会查看的代码时,我发现自己正在创建一个public_interface对象并返回它(您返回的匿名对象的命名版本)。

The benefit of this is minimal, except to add the understanding that anything which needs to be made public needs to be appended to the interface. 这样做的好处是最小的,除了添加了一个理解,即需要公开的任何内容都需要附加到接口。

The way you're currently using it: 你目前使用它的方式:

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

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

Is no better or worse than literals, because you can just as easily do this: 没有比文字更好或更差,因为你可以轻松地做到这一点:

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

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

Until you hit something which doesn't work for you, work with what fills your needs. 在你找到一些对你不起作用的东西之前,先处理满足你需求的东西。

Personally, I'll typically build any data-only objects as literals: config-objects, objects which come in from other connections, etc... 就个人而言,我通常会将任何仅数据对象构建为文字:config-objects,来自其他连接的对象等等......

Any dirt-simple object which requires maybe one or two methods, and can be built by nesting only one or two levels deep, I might build literally, as well (as long as it doesn't require initialization). 任何需要一两种方法的简单易受污染的对象,并且可以通过仅嵌套一层或两层深度来构建,我也可以逐字构建(只要它不需要初始化)。

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

If I needed several of these, perhaps I'd make a constructor. 如果我需要其中的几个,也许我会做一个构造函数。
But if I only ever needed one of something unique like this, wouldn't it save me some headaches to just do something like this: 但是,如果我只需要这样一个独特的东西,那么这样做会不会让我感到头疼:

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

If there were more-advanced calculations required, I could keep any extra vars private, and work on them from the inside. 如果需要更高级的计算,我可以将任何额外的变量保密,并从内部处理它们。
If I needed to have sensitive data inside of an object, and functions to work on that data, then I could have public-functions which call those private functions, and return results, rather than providing access. 如果我需要在对象中包含敏感数据,并且需要处理该数据的函数,那么我可以使用调用这些私有函数的公共函数,并返回结果,而不是提供访问。

Also, if I refactor my code, and decide to rename rectangle at some point, then any functions nested 3 or more deep, which are referring to rectangle will have to be modified, as well. 此外,如果我重构我的代码,并决定在某一点重命名rectangle ,那么任何嵌套3或更深的函数(也指向rectangle都必须进行修改。
Again, if you're structuring your methods so that they don't need to directly ask for any object which is further up than this , then you won't have this problem... 同样,如果你正在构造你的方法,以便他们不需要直接询问任何比this更远的对象,那么你就不会有这个问题......

...but if you have an interface that looks like: ...但是如果你有一个看起来像这样的界面:

MyApp.myServices.webService.send();

and it's expecting to find: 而且它期望找到:

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

If you change the structure of your appData module, you're going to have all kinds of errors in your webService module, until you find every reference to the old format, and rename them all. 如果更改appData模块的结构,则会在webService模块中出现各种错误,直到找到对旧格式的每个引用,并将它们全部重命名。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM