2012-03-19 2 views
21

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

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

+2

Просто помните: преждевременная оптимизация - это корень всего зла. Видео обсуждает, что делает JavaScript более совершенным, однако большинство JavaScript никогда не нуждается в оптимизации. Если у вас есть вызов функции, настроенной асинхронно после нажатия пользователем кнопки, пользователь никогда не заметит разницу между скоростью ответа 30 мс и скоростью ответа 40 мс. – zzzzBov

+1

Ответы здесь хорошие. Помните, однако, что видео - пару лет, и может быть не так важно, как когда-то было. –

ответ

20

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

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

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

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

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

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

+0

Это правильно, потому что Closures хранит ссылки на локальные переменные, а не копии, поэтому он заставляет стек перемещаться, а не локальный поиск. –

+0

Я имею в виду закрытие, которое не имеет доступа к переменным вне области видимости. Казалось, он немного лишнее, чтобы он сказал, чтобы избежать доступа к переменным вне сферы видимости, а затем явно сказать, избегать закрытий. По-видимому, подразумевается, что он означает помимо использования локальных переменных, вы также должны избегать замыканий. Я неверно истолковываю то, что он говорит? – mowwwalker

+2

@Walkerneo: Это не закрытие, если вы не ссылаетесь на нелокальные переменные. Он говорит, что вам следует избегать замыканий, когда это возможно, но когда вы * имеете * для ссылки на переменную вне области видимости, вы должны использовать локальную переменную, чтобы ускорить поиск (если вы часто используете переменную из этой функции - - иначе это не будет делать много). –

9

The linked video explains why closure can inflict some performance hits starting about 11:08.

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

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

  1. поиск локального объекта области видимости
  2. если 1 не работает, поиск объекта родительской области
  3. если 2 не работал, поиск объекта родителя сфера родителя
  4. продолжать поиск родительских областей до
  5. вы ищете глобальный масштаб
  6. , и если он еще не найден, введите неопределенную переменную ошибку.

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

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 должен сохранить то, что было принято как аргумент в родительской функции даже после того, как родительская функция уже была вызвана. Это означает, что интерпретатор должен хранить в памяти то, что вы передали, в каждую функцию, которую вы вызвали до сих пор.

+0

Будет ли хранить 'y' и' z', поскольку локальные переменные в третьей функции все же снижают производительность? Конечно, для этого примера нет причин для создания закрытий, но как насчет того, когда есть? – mowwwalker

+0

@Walkerneo Ему все равно придется подниматься по цепочке областей видимости, чтобы найти родительские переменные. Если вы используете родительские переменные более одного раза во внутренней функции, это будет стоить объявить как локальные переменные, но если вы используете их только один раз, нет ничего полезного в объявлении их как локальных переменных. –

+0

Хорошо, я понимаю проблему с пересечением цепочки областей видимости, но, как я уже сказал в своем комментарии к другому ответу, способ, которым он «экономно закрывал», сразу после того, что он говорит о переменных вне сферы видимости, кажется, например, есть что-то по своей сути неправильно с закрытием, а не только для того, чтобы добавить еще один объект в цепочку видимости. – mowwwalker