不久前,我提供了一个JavaScript设计模式(模块模式 - 见下文),这是我从John Resig示例中得到的,作为解决某人问题的一部分,我收到了以下评论:
"...这种模式有点过分了 工程设计,不是那么好。还 泄漏到全球范围内。和你的 不向异步加载器开放自己。 但它比临时更好 编码!
所以。。。
如果"泄漏"到全局范围意味着"您的对象被附加到浏览器窗口(对象)"......然后所有内容都已经附加(全局):
这"泄漏"到全球范围:
window.jQuery
。只需调用:window.jQuery
,它解析为函数();
这"泄漏"到全球范围:
function HelloWorld() { alert(‘Howdy’); }
。只需拨打:window.HelloWorld()
,你就会得到"你好"。
这"泄漏"到全球范围:
var myVariable = 10;
。只需致电:window.myVariable
,您将获得 10
如果评论者是正确的,那么上述所有"泄漏"到全局范围内。 因此,就个人而言,我看不到不"泄漏"到全局范围的方法,因为即使您的表单控件也存在在那里(以及)。
因此,这是我的问题...
- "泄漏"是什么意思全球范围?
- 为什么这很糟糕?
- 你如何避免它?
- 想要创建持久化时自定义对象,为什么模块模式(下图)不好?
- 设计模式让您封装复杂的逻辑,就是封装突然变坏只是因为我们是用JavaScript编写?
- 或。。。这个评论者是不是错了?
这是我上面提到的模块模式:
<script type="text/javascript">
var myNamespace = (function($) {
var publicInstances = {};
// ***********************
// myObject
publicInstances.myObject = myObject;
function myObject() {
/// <summary>A pointer to this</summary>
var self = this;
this.someProperty = new String();
this.initialize = function() {
/// your code here
}
this.someMethod = function() {
/// your code here
}
self.initialize();
}
return publicInstances;
})(jQuery);
jQuery(document).ready(function() {
// Use would look like
var myInstance = new myNamespace.myObject();
});
</script>
已更新:
我对下面的答案感到满意,并感谢大家抽出宝贵时间发表评论。
回顾以下答案:
当局部作用域中使用的某些内容无意中可供全局作用域使用时(例如窗口对象),就会发生"泄漏"到全局作用域。 这很糟糕,因为它会打开页面以显示潜在的命名冲突,这可能会导致变量解析为意外的值或类型。
故意使变量全局不被视为"泄漏"。 但是,需要正确命名对象间距,以减少上述命名冲突的可能性。
您无法避免全局范围的变量,但可以通过使用 RequireJS 或 Curl 等插件中提供的异步加载器和定义模块来降低上述风险。
泄漏"到全局范围是指本地范围中使用的内容无意中可用于全局范围。 这意味着分配给当前作用域中尚未定义的变量:
function myFunction() {
a=1;
}
myFunction();
alert(a);
//-> 1
这很糟糕,因为可能存在命名冲突,从而导致变量的值/类型与预期不同。 当您忘记对for
语句中使用的变量使用 var
关键字时,它还可能导致旧版 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
和require
是全局的。当然,这些只是说明性的例子,因为在 RequireJS 中定义/使用模块有许多不同的风格。