Подтвердить что ты не робот

Javascript - Использовать закрытие экономно?

Я смотрю http://www.youtube.com/watch?v=mHtdZgou0qU, и примерно в 13:37 (хе-хе) он показывает слайд списка вещей, которого следует избегать из-за добавления нового объекта в цепочку областей видимости.

Я понимаю, что он говорит с операторами using и try-catch, а также получает доступ к переменным за пределами области видимости, но я не понимаю, почему следует избегать закрытия. Если локальные переменные замыкания будут находиться в верхней части цепочки областей видимости, где потеря производительности?

4b9b3361

Ответ 1

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

Отчасти потому, что вы часто видите такой код в популярных JS-библиотеках:

(function(window, document, undefined) {
  // ...
})(window, document);

Здесь window и document становятся локальными переменными, поэтому поиск их становится намного быстрее, что становится довольно заметным, если вы ссылаетесь на эти объекты тысячи раз из вашего кода.

Эта страница содержит действительно подробное описание цепей областей и контекстов выполнения. (Вся статья интересна, если у вас есть время ее прочитать.)

В целом, однако, современные браузеры оптимизируют все эти вещи до такой степени, что они в принципе незначительны, поэтому я не беспокоюсь о них.

Ответ 2

Связанное видео объясняет, почему закрытие может нанести некоторые хиты производительности, начиная примерно с 11:08.

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

Чтобы найти значение, связанное с переменной, Javascript interprer следует этому процессу:

  • поиск объекта локальной области
  • Если 1 не работал, найдите объект родительской области.
  • Если 2 не работает, выполните поиск родительского объекта области видимости
  • продолжать поиск родительских областей до
  • вы просматриваете глобальную область
  • и если он все еще не найден, введите переменную undefined.

В нормальной функции, чтобы найти переменную, вам нужно искать только верхнюю часть цепочки областей видимости. Заключение, с другой стороны, для поиска родительских переменных придется искать в цепочке видимости, иногда на нескольких уровнях. Например, если у вас были подобные закрытия (обратите внимание, что это очень надуманный пример):

function a (x) {
  function b (y) {
    return (function (z) {
      return x + y + z;
    })(y + y);
  }
  return b(x + 3);
}

Из самой внутренней функции, чтобы оценить выражение x + y + z, она должна пересечь три уровня в цепочке видимости, чтобы найти x, тогда она должна снова пройти цепочку областей видимости на два уровня, чтобы найти y, а затем, наконец, один раз, чтобы найти z. В общем, он должен был искать шесть объектов в цепочке областей видимости, чтобы вернуть окончательный результат.

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

Также обратите внимание, что в Javascript есть значительные накладные расходы при создании функций, особенно закрытие. Возьмем, к примеру, это довольно простое замыкание:

function a(x) {
  return function (y) {
    return x + y;
  }
}

И вы называете это несколько раз, например

var x = a(1);
var y = a(2);
var z = a(3);
alert(x(3)); // 4
alert(y(3)); // 5
alert(z(3)); // 6

Вы заметите, что функция, возвращаемая из a, должна сохранять то, что было передано в качестве аргумента в родительской функции, даже после того, как родительская функция уже была вызвана. Это означает, что интерпретатор должен хранить в памяти то, что вы передали, в каждую функцию, которую вы вызывали до сих пор.