Что означает "утечка" в глобальную сферу?
Некоторое время назад я предложил шаблон дизайна JavaScript (шаблон модуля - см. ниже), который я получил от примера Джона Ресига как часть решения для someones question, и я получил следующий комментарий:
"... эта схема немного выше спроектирован и не очень хорош. Все еще просачиваясь в глобальный охват. и ваш не открывая асинхронные погрузчики. Но лучше тогда просто ad-hoc кодирование!"
Так...
Если "утечка" в глобальную область означает "ваш объект добавляется в окно браузера (объекта)"... то все уже добавляется (глобально):
Это "утечка" в глобальном масштабе:
window.jQuery
... просто звоните: window.jQuery
и он решается как функция();
Это "утечка" в глобальном масштабе:
function HelloWorld() { alert(‘Howdy’); }
... просто звоните: window.HelloWorld()
, и вы получите "Howdy.
Это "утечка" в глобальном масштабе:
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>
<ч/" > ОБНОВЛЕНО:
Я удовлетворен ответами ниже и хочу поблагодарить всех за то, что они нашли время для комментариев.
ЗАПИСАТЬ ОТВЕТЫ НИЖЕ:
" Утечка" в глобальную область возникает, когда что-то, используемое в локальной области, непреднамеренно предоставляется глобальному охвату (например, оконному объекту). Это плохо, потому что он открывает страницу для потенциальных коллизий имен, что может привести к переходу переменных на неожиданные значения или типы.
Преднамеренное создание переменной global не считается "утечкой". Тем не менее, для уменьшения вероятности упомянутых коллизий именования требуется надлежащее размещение имен объекта.
Вы не можете избежать переменных с глобальным охватом, но вы можете уменьшить вышеуказанные риски, используя асинхронные загрузчики и определяющие модули, доступные в плагинах, например RequireJS или Curl.
Ответы
Ответ 1
[[Рассказ]]
Не делайте глобальных переменных когда-либо и используйте загрузчик асинхронного модуля, например 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):
})();
Ответ 2
"Утечка" в глобальную область действия - это когда что-то, используемое в локальной области, непреднамеренно доступно для глобальной области. Это означает назначение переменной, не указанной в текущей области:
function myFunction() {
a=1;
}
myFunction();
alert(a);
//-> 1
Это плохо, потому что могут возникать конфликты имен, приводящие к переменным с разными значениями/типами, чем ожидалось. Это также может привести к ошибке в старых Internet Explorers, когда вы забудете использовать ключевое слово var
для переменной, используемой в инструкции for
.
Я бы не умышленно делал переменную global "утечкой", потому что это больше похоже на то, что вы "заливаете" ее в глобальную область. Тем не менее, это по-прежнему часто считают неправильной практикой некоторыми (хотя я думаю, что это немного мелодраматично), потому что по-прежнему существуют потенциальные столкновения имен с текущими свойствами объекта window
или переменными, заданными другими скриптами и библиотеками.
Ответ 3
Ваш модуль только "просачивает" его держатель пространства имен, поэтому он довольно приемлем.
Ответ 4
Пример загрузчика с использованием RequireJS:
Определите модуль utilities в 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 ]));
});
У вас все еще есть пространства имен и по-прежнему сохраняйте их теплыми и уютными внутри модулей:
namespace.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.