2010-06-17 2 views
9

Вопрос: Кажется, что существует множество преимуществ для закрытий, но каковы негативы (проблемы с утечкой памяти? Проблемы с запутыванием?), Увеличение полосы пропускания?)? Кроме того, правильно ли я понимаю, что Closures? Наконец, как только замыкания создаются, они могут быть уничтожены?Javascript Closures - Каковы негативы?

Я читал немного о закрытии Javascript. Надеюсь, что кто-то немного более осведомлен будет руководить моими утверждениями, исправляя меня, где неправильно.

Преимущества Closures:

  1. инкапсулировать переменные в локальной области, используя внутреннюю функцию. Анонимность функции несущественна.

Что я нашел полезным, чтобы сделать некоторые основные тестирования, о местном/глобальном масштабе:

<script type="text/javascript"> 

    var global_text = ""; 
    var global_count = 0; 
    var global_num1 = 10; 
    var global_num2 = 20; 
    var global_num3 = 30; 

    function outerFunc() { 

     var local_count = local_count || 0; 

     alert("global_num1: " + global_num1); // global_num1: undefined 
     var global_num1 = global_num1 || 0; 
     alert("global_num1: " + global_num1); // global_num1: 0 

     alert("global_num2: " + global_num2); // global_num2: 20 
     global_num2 = global_num2 || 0;   // (notice) no definition with 'var' 
     alert("global_num2: " + global_num2); // global_num2: 20 
     global_num2 = 0; 

     alert("local_count: " + local_count); // local_count: 0 

     function output() { 
     global_num3++; 

     alert("local_count: " + local_count + "\n" + 
       "global_count: " + global_count + "\n" + 
       "global_text: " + global_text 
      ); 

     local_count++; 
     } 

     local_count++; 
     global_count++; 

     return output; 
    } 

    var myFunc = outerFunc(); 

    myFunc(); 
     /* Outputs: 
     ********************** 
     * local_count: 1 
     * global_count: 1 
     * global_text: 
     **********************/ 

    global_text = "global"; 
    myFunc(); 
     /* Outputs: 
     ********************** 
     * local_count: 2 
     * global_count: 1 
     * global_text: global 
     **********************/ 

    var local_count = 100; 
    myFunc(); 
     /* Outputs: 
     ********************** 
     * local_count: 3 
     * global_count: 1 
     * global_text: global 
     **********************/ 


    alert("global_num1: " + global_num1);  // global_num1: 10 
    alert("global_num2: " + global_num2);  // global_num2: 0 
    alert("global_num3: " + global_num3);  // global_num3: 33 

</script> 

Интересные вещи, которые я вынес из него:

  1. предупреждения в outerFunc вызываются только один раз, то есть когда вызов externalFunc назначается myFunc (myFunc = outerFunc()). Это назначение, по-видимому, сохраняет openFunc открытым, в том, что я хотел бы назвать постоянным состоянием.

  2. Каждый раз, когда вызывается myFunc, выполняется возврат. В этом случае возврат является внутренней функцией.

  3. Что-то действительно интересное - это локализация, возникающая при определении локальных переменных. Обратите внимание на разницу в первом предупреждении между global_num1 и global_num2, даже до того, как переменная пытается быть создана, global_num1 считается неопределенным, потому что «var» используется для обозначения локальной переменной этой функции. - Об этом говорили раньше, в порядке работы для механизма Javascript, это просто приятно видеть, что это работает.

  4. Глобалы все еще могут использоваться, но локальные переменные будут перекрывать их. Обратите внимание, что перед третьим вызовом myFunc создается глобальная переменная с именем local_count, но она не влияет на внутреннюю функцию, которая имеет переменную, которая имеет одно имя. И наоборот, каждый вызов функции имеет возможность изменять глобальные переменные, как заметил global_var3.

сообщение Мысли: Даже если код прост, он завален на уведомления для вас, ребята, так что вы можете подключить и играть.

Я знаю, что есть другие примеры замыканий, многие из которых используют анонимные функции в сочетании с петлевыми структурами, но я думаю, что это хорошо для 101-стартового курса, чтобы увидеть эффекты.

Единственное, что меня беспокоит, это негативное воздействие на память. Поскольку он сохраняет открытую функциональную среду, он также сохраняет эти переменные, хранящиеся в памяти, что может/не иметь последствий для производительности, особенно в отношении обходов DOM и сбора мусора. Я также не уверен, какую роль это будет играть в плане утечки памяти, и я не уверен, что закрытие можно удалить из памяти простым «delete myFunc ;."

Надеется, что это помогает кому-то,

vol7ron

+3

Я попкорн для этого. – Pointy

ответ

6

Вы можете получить плот хороших ответов. Один определенного негатив утечки циклической памяти Internet Explorer. В основном,„круговая“ссылка на объекты DOM является не признаются в качестве коллекционных по JScript. Это легко создать то, что IE считает циклическую ссылку с помощью затворов. Некоторые примеры приведены во второй ссылке.

В IE6, единственный способ освободить память, чтобы завершить весь процесс. В IE7 они улучшили его, так что, когда вы перемещаетесь от рассматриваемой страницы (или закрываете ее), память восстанавливается. В IE8 объекты DOM лучше понятны JScript и собираются так, как вы ожидали, они должны быть.

Предлагаемый обходной путь для IE6 (помимо завершения процесса!) Заключается не в использовании замыканий.

+1

Было не так много ответов, поэтому вы получили голосование. Я не слишком беспокоюсь об IE6. Согласно (http://www.w3schools.com/browsers/browsers_stats.asp), он по-прежнему используется 7%, но я полагаю, что в ближайшие месяцы значительно сократится. Правительство является огромным вкладом в более старые версии IE, как только они используют новую версию, многие частные подрядчики, вероятно, перестанут поддерживать и использовать ее. Я думаю, что за последние пару месяцев многие правительственные агентства переключились на более новый, более безопасный браузер, особенно после этой угрозы Adobe/IE6. – vol7ron

+0

Я немного удивлен отсутствием ответов. Я беру вашу точку зрения на IE6; но, к сожалению, я все еще вижу около 25% IE6 среди посетителей корпоративных веб-приложений, над которыми я работаю. Некоторые из этих огромных предприятий настолько консервативны, что они до сих пор доят «утомительную стоимость» от своих инвестиций в Windows XP и IE6 вместе с ним. И они в разной степени взвешивают, прогрессивные усилия интернет-производителей, которые тем не менее все еще хотят своего бизнеса. Я действительно надеюсь, что он умрет в ближайшие месяцы, но я не был бы шокирован, если в 2016 году будут большие предприятия, которые все еще работают с IE6. –

0

Закрытие может привести к утечке памяти, однако Mozilla предприняла попытки оптимизировать механизм сбора мусора, чтобы предотвратить это.

Я не уверен, как Chrome закрывает ручки. Я думаю, что они на одном уровне с Mozilla, но я не хочу говорить точно. IE8 определенно улучшен по сравнению с более ранними версиями IE - это почти совершенно новый браузер, есть еще некоторые нюансы.

Вы также должны проверить код, чтобы увидеть, есть ли какие-либо улучшения в скорости.

6

Закрытие приносит много преимуществ ... но также и количество исправлений. То же самое, что делает их мощными, также делает их вполне способными создать беспорядок, если вы не будете осторожны.

Помимо проблемы с круговыми ссылками (что на самом деле не так много проблемы, поскольку IE6 вряд ли используется на всех за пределами Китая), существует хотя бы один огромный отрицательный потенциал: Они могут усложнять область действия. При использовании хорошо, они улучшают модульность и совместимость, позволяя функциям обмениваться данными, не подвергая их воздействию ... но при неправильном использовании может оказаться трудным, если не невозможным точно определить, где переменная установлена ​​или изменена.

JavaScript без закрытия имеет три области * для переменных: уровне блока, уровня функций и глобальных. Объектной области не существует. Без замыканий вы знаете, что переменная объявляется в текущей функции или в глобальном объекте (потому что там живут глобальные переменные).

С закрытием у вас больше нет такой гарантии. Каждая вложенная функция вводит другой уровень видимости, и любые замыкания, созданные в этой функции, видят (в основном) те же переменные, что и содержащая функция. Большая проблема заключается в том, что каждая функция может определять свои собственные переменные по своему усмотрению, которые скрывают внешние.

Использование закрытий должным образом требует, чтобы вы (а) знали, как замыкания и var влияют на область видимости, и (б) отслеживать, в какой области видимости находятся ваши переменные. В противном случае переменные могут быть случайно разделены (или потеряны псевдопеременные !), и могут возникнуть всякие глупости.


Рассмотрим следующий пример:

function ScopeIssues(count) { 
    var funcs = []; 
    for (var i = 0; i < count; ++i) { 
     funcs[i] = function() { console.log(i); } 
    } 
    return funcs; 
} 

Короткий, простой ... и почти наверняка сломаны. Часы:

x = ScopeIssues(100); 

x[0](); // outputs 100 
x[1](); // does too 
x[2](); // same here 
x[3](); // guess 

Каждая функция в выводах массива count. Что тут происходит? Вы видите эффекты объединения замыканий с непониманием замкнутых переменных и областей.

При создании замыканий они не используют значение i в момент их создания, чтобы определить, что выводить. Они используют переменную i, которая совместно используется внешней функцией и по-прежнему меняется. Когда они выводят его, они выводят значение со времени, которое оно называется. Это будет равно count, значение, которое заставило цикл остановиться.

Чтобы исправить это, вам понадобится другое закрытие.

function Corrected(count) { 
    var funcs = []; 
    for (var i = 0; i < count; ++i) { 
     (function(which) { 
      funcs[i] = function() { console.log(which); }; 
     })(i); 
    } 
} 

x = Corrected(100); 

x[0](); // outputs 0 
x[1](); // outputs 1 
x[2](); // outputs 2 
x[3](); // outputs 3 

Другой пример:

value = 'global variable'; 

function A() { 
    var value = 'local variable'; 
    this.value = 'instance variable'; 
    (function() { console.log(this.value); })(); 
} 

a = new A(); // outputs 'global variable' 

this и arguments различны; в отличие от почти всего остального, они не разделены по границам закрытия ?. Каждый вызов функции переопределяет их - и если вы не вызовите функцию как

  • obj.func(...),
  • func.call(obj, ...),
  • func.apply(obj, [...]) или
  • var obj_func = func.bind(obj); obj_func(...)

указать this, то вам 'Получает значение по умолчанию для this: глобальный объект. ^

Наиболее распространенной идиомой, чтобы обойти this вопросе объявить переменную и установите его значение в this. Наиболее распространенные имена, которые я видел, - that и self.

function A() { 
    var self = this; 
    this.value = 'some value'; 
    (function() { console.log(self.value); })(); 
} 

Но что делает self реальные переменное, со всеми потенциальными нечетностями, что влечет за собой. К счастью, редко бывает необходимо изменить значение self без переопределения переменной ... но внутри вложенной функции переопределение self, конечно, переопределяет ее для всех функций, вложенных в нее. И вы не можете сделать что-то вроде

function X() { 
    var self = this; 
    var Y = function() { 
     var outer = self; 
     var self = this; 
    }; 
} 

из грузоподъемных. JavaScript эффективно перемещает все объявления переменных в начало функции.Это делает выше код, эквивалентный

function X() { 
    var self, Y; 
    self = this; 
    Y = function() { 
     var outer, self; 
     outer = self; 
     self = this; 
    }; 
} 

self уже локальная переменная outer = self работает, так outer получает локальное значение - что на данный момент, является undefined. Вы только что потеряли свою ссылку на внешний self.


* As ES7. Раньше их было всего два, и переменные были еще проще отслеживать. : P

? Функции, объявленные с использованием синтаксиса лямбда (новые для ES7), не переопределяют this и arguments. Что еще более усложняет этот вопрос.

^Новые интерпретаторы поддерживают так называемый «строгий режим»: функция выбора, которая направлена ​​на то, чтобы определенные шаблоны кода iffy либо полностью сбой, либо нанесли ущерб. В строгом режиме, this по умолчанию является undefined, а не глобальным объектом. Но это все еще какая-то другая ценность, чем вы обычно намеревались возиться.

+1

Очень приятно, но я думаю, что у вас небольшая опечатка в последнем бит. «' outer' уже является локальной переменной ... «Я думаю, что« внешний »должен быть« сам »? –

+0

@JakeKing: Упс ... исправлено. Спасибо :) – cHao

+0

upvote для ясного примера подъема, вызывающего неожиданное поведение. –