如何构建你的 JavaScript 代码?模块模式

Avatar of Chris Coyier
Chris Coyier 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200美元免费额度!

JavaScript 的有趣之处在于它不会强加任何特定的结构。如果你愿意,可以称之为“自带组织”。随着我在 Web 应用网站中编写越来越多的 JavaScript 代码,这一点变得越来越有趣。 如何构建你的 JavaScript 代码非常重要,因为

  1. 如果做得好,它可以让其他人和重新查看自己代码的你更容易理解代码。
  2. 拥有一个既定的结构有助于保持未来代码的整洁,并鼓励你遵循已声明的最佳实践。
  3. 它使你的代码可测试。

在继续之前,我想说明一点,我远不是 JavaScript 大师。JavaScript 世界里有很多聪明人,他们比我早得多就开始做这件事,并讨论这件事,那时我甚至还不知道 div 是什么。

最近我一直在思考这个问题,因为我为 CodePen 编写了大量的 JavaScript 代码,CodePen 非常依赖 JavaScript。从一开始我们就一直在使用“模块模式”,我认为它一直为我们提供了很好的服务。

然后我发布了这篇关于 Treehouse 广告制作过程 的文章,在文章中我也使用了模块模式来编写 JavaScript 代码。Louis Lazaris 评论说,最近他也喜欢这样编写 JavaScript 代码,并 引发了一些讨论。Louis 随后发表了一篇 文章,解释了他喜欢的结构模式。

概念

如果有一个可以从中构建的示例,则更容易理解这样的概念。假设我们正在构建一个新闻小部件。它列出了一些新闻文章,并且有一个按钮可以加载更多新闻文章。

模块

谦逊的开端

var NewsWidget = {

};

只是一个对象。我喜欢我的 JavaScript 变量以小写字母开头,但模块的首字母大写。这只是一个有助于代码可读性的约定。

设置

新闻小部件可能与一些重要的数字相关联(例如,其中包含的文章数量)。此外,还有一些需要定期访问的重要元素(DOM 节点)。

此模块将有多个执行小型特定操作的子函数。其中许多可能需要访问这些设置和“缓存”的元素。因此,我们将使每个子函数都可以访问这些设置。

var s,
NewsWidget = {

  settings: {
    numArticles: 5,
    articleList: $("#article-list")
  }

};

我们将在稍后讨论子函数访问。

初始化函数

为了“启动”,我们将只调用**一个**函数。这在所有模块中都将保持一致,因此当您开始阅读代码并弄清楚发生了什么时,您会确切地知道在哪里查找。

var s,
NewsWidget = {

  settings: {
    numArticles: 5,
    articleList: $("#article-list"),
    moreButton: $("#more-button")
  },

  init: function() {
    // kick things off
    s = this.settings;
  }

};

init 函数首先要做的事情是将变量 s(我们在与模块相同级别声明了该变量)设置为指向设置的指针。由于 s 的声明位置,这意味着模块的所有子函数都可以访问设置。很好。

绑定 UI 操作

Alex Vasquez 为我们在 CodePen 建立了一个约定,即我们会有一个专门用于绑定 UI 事件的函数。在绑定时,您永远不会编写任何“执行操作”的代码,您只需进行绑定,然后调用另一个名称合适的子函数。

var s,
NewsWidget = {

  settings: {
    numArticles: 5,
    articleList: $("#article-list"),
    moreButton: $("#more-button")
  },

  init: function() {
    s = this.settings;
    this.bindUIActions();
  },

  bindUIActions: function() {
    s.moreButton.on("click", function() {
      NewsWidget.getMoreArticles(s.numArticles);
    });
  },

  getMoreArticles: function(numToGet) {
    // $.ajax or something
    // using numToGet as param
  }

};

合并文件

新闻小部件可能不是页面上唯一的 JavaScript 代码。您需要加载许多模块。也许新闻小部件位于您网站的每个页面上,因此属于 global.js 文件。此 global.js 文件最终将是一堆连接在一起的模块,然后是一个大型启动派对。

处理此连接操作有很多方法。它可能是像 CodeKit 这样的开发工具,以及它进行 附加/前置 的能力。它也可能是某些花哨的 Grunt.js 构建脚本。

在 CodePen,我们使用 Ruby on Rails 及其 资产管道。因此,对我们来说,global.js 文件将类似于

//= require common/library.js

//= require module/news-widget.js
//= require module/some-other-widget.js

(function() {

  NewsWidget.init();

  SomeOtherModule.init();

})();

就是这样

我认为这是一种非常令人满意的编写 JavaScript 代码的方式。它满足了我之前提到的关于结构重要性的三个要点。

关于测试点,我知道没有过多讨论,但我相信您可以想象,在负责特定任务的较小的特定函数中,编写断言会更容易。大多数 JavaScript 测试都是这样完成的(例如 Jasmine)。例如,在某种形式的代码中:“我断言,当此函数获取此值时,另一件事会发生,并且等于某个其他值。”

这只是冰山一角。我刚刚开始接触它,但 Addy Osmani 编写的 学习 JavaScript 设计模式(可在网上免费阅读!)看起来会深入探讨这个问题。