在这种情况下创建匿名函数的主要原因是为了防止全局对象污染。这不是真正的模块模式。
当你声明一个变量时,问题就出现了。如果没有函数范围,变量将被添加到全局对象(窗口)中。如果您要在函数中声明变量。它会将变量添加到函数范围内,而不会污染全局对象窗口。
发生的情况是,一个 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/ 定义的模块。这是非常基本的,因为它没有考虑模块依赖关系,所以如果bar 在foo 之前加载和使用。那么require方法将无法返回模块。
另外,如果您想存储模块而不让它们在全局范围内可见,您只能在窗口对象上定义 require 和 define 方法并将模块隐藏到匿名范围中,如下所示:
(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)
这样,define 和 require 是唯一可以让您访问模块的函数。其他模块将无法修改其他模块而不需要它们。这在使用可能与您的模块系统冲突的第三方脚本时很有用。