【问题标题】:How to call/use this module in other JS files/modules如何在其他 JS 文件/模块中调用/使用该模块
【发布时间】:2015-08-19 15:55:25
【问题描述】:

我最近阅读了一些 JS 模块设计模式。我遇到了这个小代码sn-p,如下所示。

(function(window) {
    var Module = {
        data: "I'm happy now!"
    };

    window.Module = Module;
})(window);

还是不太明白这段代码,我的问题是:

  • 如何在这个特定的 JS 文件之外使用/调用这个模块?需要我 给这个模块分配一个变量?例如var module1 = (...)(...);
  • 谁能解释一下这里的window参数代表什么?
  • 将两/三个此类模块放在 同一个文件?

【问题讨论】:

  • 在我看来,这可能是一切,但不是模块设计模式。
  • @connexo 在实践中,它是一种模块设计模式,因为将代码范围限定为函数是防止意外全局范围污染的第一步。

标签: javascript function design-patterns


【解决方案1】:

在这种情况下创建匿名函数的主要原因是为了防止全局对象污染。这不是真正的模块模式。

当你声明一个变量时,问题就出现了。如果没有函数范围,变量将被添加到全局对象(窗口)中。如果您要在函数中声明变量。它会将变量添加到函数范围内,而不会污染全局对象窗口。

发生的情况是,一个 javascript 文件可以添加一个名为 foo 的变量,并且在另一个文件上也使用名为 foo 的变量。除非你真的想让两个 javascript 文件共享一个变量,否则它可能会产生难以修复的冲突和错误。

例如:a.js

var foo = "one"

b.js

var foo = "two"

c.js

alert(foo)

在这种情况下,警报框可能会显示“一”或“二”,具体取决于包含的 javascript 文件中的顺序。

但是有了这个 a.js:

(function () {
    var foo = "one"
})()

b.js

(function () {
    var foo = "two"
})()

c.js

(function () {
    alert(foo)
})()

这会产生错误,因为您不能 alert 未声明的变量。

检测未定义变量的一种方法是确保以严格模式执行javascript代码。

为此,请在文件或函数的顶部添加字符串 "use strict"

function () {
  "use strict"
  ...
}

未声明的变量会引发错误,应该可以通过这种方式修复代码。

此外,如果您忘记使用 var 关键字声明变量,即使代码的作用域是函数,它也可能最终将变量添加到全局作用域。防止全局范围污染的唯一方法是在严格模式下运行代码。

在您提供的代码 sn-p 中,名为 Module 的模块被显式添加到窗口对象中。您可以从 javascript 中的任何位置访问窗口对象,除非窗口名称被其他变量隐藏。

现在,回到模块。如果要定义模块,可以通过多种方式完成。例如,您可以在称为模块的窗口对象上创建一个对象。在此对象中,您将插入您的模块。

模块.js

window.modules = {}

foo.js

(function (window) {
  var module = {}
  ...
  window.modules.foo = module
})(window)

这个变体不是很好,因为你必须手动将模块添加到modules 对象。您必须手动修改窗口对象,这可能会出错。

modules.js

window.modules = {}

function define(name, constructor) {
  var module = {exports: {}}
  constructor(module)
  window.modules[name] = module.exports
}

function require(name) {
  return window.modules[name]
}

foo.js

define("foo", function (module) {
  module.exports.one = function () {
    return 1
  }

  module.exports.plus = function (a, b) {
    return a + b
  }
})

bar.js

define("bar", function (module) {
  module.exports = function () {
    var foo = require("foo")
    return foo.plus(foo.one(), foo.one())
  }
})

这是一个模块定义,看起来有点像用http://requirejs.org/ 定义的模块。这是非常基本的,因为它没有考虑模块依赖关系,所以如果barfoo 之前加载和使用。那么require方法将无法返回模块。

另外,如果您想存储模块而不让它们在全局范围内可见,您只能在窗口对象上定义 requiredefine 方法并将模块隐藏到匿名范围中,如下所示:

  (function (window) {
    var modules = {}

    function define(name, constructor) {
      var module = {exports: {}}
      constructor(module)
      modules[name] = module.exports
    }

    function require(name) {
      return modules[name]
    }

    window.define = define
    window.require = require
  })(window)

这样,definerequire 是唯一可以让您访问模块的函数。其他模块将无法修改其他模块而不需要它们。这在使用可能与您的模块系统冲突的第三方脚本时很有用。

【讨论】:

  • 您的回答对我了解如何避免全局变量污染确实很有帮助。为了使示例更加完整,您是否还可以添加一些关于如何从 a.js 或 b.js 公开 foo 的代码,以便 c.js 可以访问 foo。我相信对于其他读者/js新手理解这个话题也会更有用。谢谢。
  • @catlovespurple 那里
  • 非常有帮助。很棒的资源!我同意它作为正确答案。谢谢!
  • @catlovespurple 谢谢
  • 非常有帮助。 :)
【解决方案2】:

实际上这不是一个模块,而是一个自调用匿名函数或一个立即函数,它在参数中获取一个对象并将Module属性分配给它:

  • 页面window是传递给这个函数的参数。

  • 因此,一个名为Module 的对象包含data 属性被分配给window。

JavaScript 自调用函数:

自动调用(启动)一个自调用表达式,而不被调用。

如果表达式是,函数表达式将自动执行 然后是()

您不能自行调用函数声明。

您必须在函数周围添加括号以表明它是 函数表达式

因此,正如您所见,不能调用即时函数,因为它的名称表明它将立即执行,并且它本身没有其他函数或作用域可以执行它。

为了更好的参考,请查看:


关于您最后一个关于它的好处和良好实践的问题,如给定的Article reference 所示:

在哪里使用自执行函数?

一个明显的情况是当你想像我一样自动运行一个函数 显示在上面的例子中,但这是微不足道的。如果你处于某种情况 你想在哪里重复运行一段代码,比如更新 基于用户交互或获取的数据库中的某些内容 每 10 秒从数据库记录一次,或者您想加载新故事 通过ajax 类似于 facebook 在其主页或其他一些 类似的情况,通常会选择setInterval 函数 像这样:

setInterval(doStuff, 10000);

在上面,doStuff 函数将每 10 秒调用一次。那就是 大多数开发人员似乎采用的正常方法。然而,有一个 巨大的问题。

setInterval 将在 10 秒的指定时间准确地调用 doStuff 函数,一次又一次地不考虑 doStuff 函数是否真的完成了它的工作 应该做的。这很糟糕,肯定会让你出乎意料 结果。

这是setInterval 是“坏”的一个例子,应该是 避免。

这正是自执行函数派上用场的地方。我们可以 在自动执行功能的帮助下完成同样的任务 setTimeout 像这样:

function foo(){
     // your other code here  
     setTimeout(foo, 10000);
}();

这段代码也会一次又一次地重复,但有一点不同。 setTimeout 永远不会被触发,除非 doStuff 完成。一个多 比在这种情况下使用 setInterval 更好的方法。

从另一个文件调用它:

如果此函数在另一个文件上,如果包含此文件,它将自动调用。

为什么我们在 JavaScript 中使用自调用函数?

如果你问自己为什么要使用这些函数,自调用函数用于管理Variable Scope

查看 answer here 了解更多信息。

【讨论】:

  • 感谢 chsdk!你能告诉我如何在这个 JS 文件之外使用这个匿名函数吗?
  • @catlovespurple 看看我编辑的答案,你会找到你正在寻找的所有解释和你问题的所有答案。
猜你喜欢
  • 1970-01-01
  • 2013-08-30
  • 2019-11-19
  • 1970-01-01
  • 1970-01-01
  • 2017-01-15
  • 2018-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多