4 回答
“泄漏”到全局作用域是指在局部作用域中使用的某些东西无意中对全局作用域可用。这意味着分配给当前范围内尚未定义的变量:
function myFunction() {
a=1;
}
myFunction();
alert(a);
//-> 1
这很糟糕,因为可能存在命名冲突,导致变量的值/类型与预期不同。var
当您忘记将关键字用于for
语句中使用的变量时,它还可能导致旧 Internet Explorer 中的错误。
我不会故意将全局变量设为“泄漏”,因为这更像是您将其“倒入”全局范围。然而,这仍然经常被一些人认为是不好的做法(尽管我认为这有点夸张),因为仍然存在与window
对象的当前属性或其他脚本和库设置的变量的潜在命名冲突。
[[短篇故事]]
永远不要创建全局变量,而是使用像requirejs或curl这样的异步模块加载器
[[很长的故事]]
那条评论的结构很糟糕。
模块系统没有任何问题。我一直在抱怨使用全局变量。(我仍然认为完整的通用模块模式很臃肿)。
您是否应该避免所有全局变量是一个不同的问题,我认为这是一个风格问题。您可以使用异步加载器来传递模块或使用window
来传递模块。
- “泄漏”到全球范围是什么意思?
我的意思是你创建全局变量。尽量减少全局变量的使用是一种模式。在函数式编程中,全局变量可能为零,但这是与使用全局模块不同的模式。
- 为什么这么糟糕?
全局拥有任何状态都可能导致该状态被破坏。
- 你如何避免它?
你不能。您可以尽量减少全局变量的数量。为了避免完全拥有全局状态,您可以使用异步加载器。这些为您定义了一些全局变量,然后您可以使用它们。
- 当想要创建持久的自定义对象时,为什么模块模式(下)不好?
模块模式没有任何问题。问题是全局存储您的模块。问题在于具有全局名称空间。
- 设计模式可以让你封装复杂的逻辑,仅仅是因为我们用 JavaScript 编写,封装突然变得糟糕了吗?
既然我已经清除了评论的意图,这个问题并不真正相关
- 或者......这个评论者完全错了吗?
该评论充其量是措辞不佳。我反对全局命名空间而不是模块,但没有正确说明这一点。
另一种方法是使用异步加载器和定义模块。这些可以缩小到两个全局变量。define
和require
。
require = function(moduleName, callback)
这将获得一个模块,然后将其返回给您。
define = function(obj)
这定义了一个模块。
这里的概念是你的多文件代码如下:
// main.js
require([
"foo.js",
"bar.js",
...,
], function(foo, bar, ...) {
// do stuff
});
//foo.js
(function() {
var namespace = modulePatternCode;
...
define(namespace):
})();
//bar.js
(function() {
var namespace = modulePatternCode;
...
define(namespace):
})();
您的模块只会“泄漏”它的名称空间持有者,因此它是可以接受的。
使用RequireJS 的加载器示例:
在 utils.js 中定义一个实用程序模块:
define(function () {
return {
each: function (iterable, callback) {
// ...
},
map: function (iterable, mapper) {
// ...
}
};
});
在另一个模块中使用上述模块,比如 math.js:
define([ "utils" ], function (utils) {
return {
sum: function (numbers) {
var sum = 0;
utils.each(numbers, function (n) {
sum += n;
});
return sum;
},
average: function (numbers) {
return this.sum(numbers) / numbers.length;
}
};
});
你可以在另一个文件中使用 math.js,比如 main.js:
console.log("About to add 1-3");
require([ "math" ], function (math) {
console.log(math.sum([ 1, 2, 3 ]));
});
你仍然可以拥有命名空间,并且仍然让它们在模块中保持温暖和舒适:
命名空间.js:
define([ "foo", "bar", "moo" ] function (foo, bar, moo) {
return {
foo: foo,
bar: bar,
moo: moo
};
});
然后其余模块可以在定义期间使用此命名空间:
define([ "namespace" ], function (namespace) {
namespace.foo(42);
});
或者在运行时,在其他一些模块中:
define(function () {
return {
initialize: function () {
require([ "namespace" ], function (namespace) {
namespace.foo(42);
});
}
};
});
在上面的用法中,只有define
andrequire
是全局的。当然,这些只是说明性示例,因为在 RequireJS 中有许多不同风格的定义/使用模块。