如何在其他JS文件/模块中调用/使用此模块

时间:2015-08-19 15:55:26

标签: javascript function design-patterns

我最近阅读了一些JS模块设计模式。我遇到了如下的小代码片段。

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

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

仍然不太了解这段代码,我的问题是:

  • 如何在此特定JS文件之外使用/调用此模块?需要我 为此模块分配变量?例如var module1 =(...)(...);
  • 有人可以解释这里的窗口参数代表什么吗?
  • 在这里安装两个/三个这样的模块是一个好习惯 同一个文件?

2 个答案:

答案 0 :(得分:9)

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

声明变量时会出现问题。如果没有函数作用域,变量将被添加到全局对象(窗口)。如果要在函数中声明变量。它会将变量添加到函数范围而不会污染全局对象窗口。

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关键字声明变量,则可能最终将变量添加到全局范围,即使代码范围限定为函数也是如此。防止全局范围污染的唯一方法是以严格模式运行代码。

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

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

module.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/定义的模块。这是非常基本的,因为它没有考虑模块依赖性,所以如果在bar之前加载和使用foo。然后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是唯一可以让您访问模块的函数。其他模块将无法在不首先要求的情况下修改其他模块。当使用可能与您的模块系统冲突的第三方脚本时,这可能很有用。

答案 1 :(得分:1)

实际上这不是一个模块,而是一个 Self-Invoking Ananymous函数或一个 Immediate函数,它在参数中获取一个对象并分配一个Module财产:

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

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

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 以获取更多信息。