我最近阅读了一些JS模块设计模式。我遇到了如下的小代码片段。
(function(window) {
var Module = {
data: "I'm happy now!"
};
window.Module = Module;
})(window);
仍然不太了解这段代码,我的问题是:
答案 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
方法将无法返回模块。
此外,如果要存储模块而不将它们显示在全局范围内,则只能在窗口对象上定义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
是唯一可以让您访问模块的函数。其他模块将无法在不首先要求的情况下修改其他模块。当使用可能与您的模块系统冲突的第三方脚本时,这可能很有用。
答案 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 以获取更多信息。