Что подразумевается под «утечкой» в глобальную сферу? - PullRequest
17 голосов
/ 10 мая 2011

Некоторое время назад я предложил шаблон проектирования JavaScript (Шаблон модуля - см. Ниже), полученный из примера Джона Резига, как часть решения чьего-то вопроса и я получил следующий комментарий:

«… этот паттерн чуть более спроектирован и не так хорош. Еще утечка в глобальном масштабе. и ваш не открывать себя асинхронным загрузчикам. Но это лучше, чем просто ad-hoc кодирование! »

Итак ...

Если «утечка» в глобальную область означает «ваш объект добавляется в окно браузера (объект)»… тогда все уже добавляется (глобально):

Это «просачивается» в глобальную область видимости:

window.jQuery

… просто вызовите: window.jQuery и он разрешается как функция ();

Это «просачивается» в глобальную область видимости:

function HelloWorld() { alert(‘Howdy’); }

... просто позвоните: window.HelloWorld() и вы получите "Привет".

Это «просачивается» в глобальную область видимости:

var myVariable = 10;

… просто позвоните: window.myVariable и вы получите 10

Если комментатор верен, то все вышеперечисленное «просачивается» в глобальную область видимости. Так что лично я не вижу способа НЕ «просочиться» в глобальную область видимости, поскольку там даже существуют ваши элементы управления формой.

Как таковые, вот мои вопросы ...

  • Что подразумевается под «утечкой» в глобальная сфера?
  • Почему это плохо?
  • Как вам этого избежать?
  • При желании создать постоянный custom-объекты, почему модуль Узор (внизу) плохой?
  • Шаблоны проектирования позволяют вам инкапсулировать сложная логика, это инкапсуляция вдруг плохо просто потому, что мы писать на 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 .

Ответы [ 4 ]

6 голосов
/ 10 мая 2011

«Утечка» в глобальную область - это когда что-то, что используется в локальной области, непреднамеренно становится доступным для глобальной области.Это означает присвоение переменной, еще не определенной в текущей области:

function myFunction() {
    a=1;
}

myFunction();
alert(a);
//-> 1

Это плохо, потому что могут быть конфликты именования, приводящие к переменным с значениями / типами, отличными от ожидаемых.Это также может привести к ошибке в старых Internet Explorer, когда вы забудете использовать ключевое слово var для переменной, используемой в операторе for.

Я бы не стал преднамеренно делать переменную глобальной, какутечка ", потому что это больше похоже на то, что вы" вливаете "его в глобальную сферу.Однако некоторые все еще считают это плохой практикой (хотя я думаю, что это немного мелодраматично), потому что все еще существуют потенциальные конфликты именования с текущими свойствами объекта window или переменными, установленными другими скриптами и библиотеками.

4 голосов
/ 10 мая 2011

[[Короткая история]]

Никогда не создавайте глобальные переменные и не используйте асинхронный загрузчик модулей, такой как 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):
})();
1 голос
/ 10 мая 2011

Ваш модуль только «протекает» из своего держателя пространства имен, поэтому он вполне приемлем.

0 голосов
/ 10 мая 2011

Пример загрузчика с использованием 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 ]));
});

У вас все еще могут быть пространства имен, и они будут оставаться теплыми и уютными внутри модулей:

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 существует множество различных вариантов определения / использования модулей.

...