Производительность закрытия Javascript
Я работаю некоторое время в javascript и обычно делаю что-то вроде этого только для кэширования значений свойств функций, объявленных внутри глубокой структуры или "пространства имен"
//global scope
(function ($, lib) {
//function scope 1
var format = lib.format, // instead of calling lib.format all the time just call format
touch = lib.pointer.touch, //instead of calling lib.pointer.touch each time just touch
$doc = $(document),
log = logger.log; //not console log...
$doc.on('app:ready', function () {
//function scope 2
$doc.on('some:event', function (e) {
//function scope 3
//use the cached variables
log(format('{0} was triggered on the $doc object', e.type);
});
$doc.on(touch, function (e) {
//function scope 3
log(format('this should be {1} and is... {0} ', e.type, touch);
});
});
}(jQuery, lib));
Я делал это, потому что:
- как ленивый, как я, писать прикосновение кажутся более привлекательными, поскольку писать lib.pointer.touch, даже когда мощные IDE с фантастическим автозаполнением могут помочь в этом, коснуться короче.
- минимизатор может преобразовать эту единственную приватную переменную в одну буквенную переменную, поэтому она также имеет смысл для меня (я знаю, я знаю, никогда не оптимизирую слишком рано, но это кажется безопасным, я думаю)
Весь написанный таким образом код, по-видимому, работает прилично на мобильных устройствах и настольных браузерах, поэтому кажется, что это безопасная практика (в "практике", каламбур). Но я с тех пор полагаюсь на замыкания, а внутренние функции должны создавать замыкание, чтобы сохранить контекст, который был объявлен мне интересно...
-
если функция не использует переменные из внешнего контекста (свободные переменные)... является ли контекст закрытия еще сохраненным? (или если дерево дерева падает в лес, и никто не слышит его, он все еще делает звук аварии? hehe) Я знаю, что это может варьироваться в зависимости от движка javascript, потому что ECMA ничего не говорит о том, требуется ли сохранить контекст или нет, когда переменные извне не доступны.
-
если указанное выражение истинно... будет ли этот блок кода более эффективным?
//global scope
(function ($, lib) {
//function scope 1
var format = lib.format,
touch = lib.pointer.touch,
$doc = $(document),
log = console.log;
$doc.on('app:ready', function () {
(function ($doc, touch, lib, format) {
// since all the variables are provided as arguments in this function
// there is no need to save them to the [[scope]] of this function
// because they are local to this self invoking function now
$doc.on('some:event', function (e) {
//function scope 3
//use the cached variables
log(format('{0} was triggered on the $doc object', e.type);
});
$doc.on(touch, function (e) {
//function scope 3
log(format('this should be {1} and is... {0} ', e.type, touch);
});
}($doc, touch, lib, format));
});
}(jQuery, lib));
Является ли он более эффективным, потому что он передает эти переменные функции непосредственного вызова? будут ли затраты на создание этой новой функции какое-либо влияние на код, (отрицательный или положительный)?
-
Как я могу правильно измерить потребление памяти в моей javascript-библиотеке надежным способом? У меня есть 100x маленьких модулей javascript, которые находятся внутри непосредственных функций самозапуска, в основном, чтобы избежать переполнения переменных в глобальном контексте. Таким образом, все они завернуты в модули, очень похожие на блок кода, упомянутый выше.
-
будет ли он лучше влиять на кеширование переменных ближе, даже если это будет означать, что мне придется повторять объявления переменных ближе к тому, где они будут использоваться?
-
У меня такое чувство, что для поиска переменной не в текущем локальном контексте, механизм сначала рассмотрит родительскую область и перечислит все переменные на этом уровне... чем больше переменных на уровень, тем хуже вероятно, будет производительностью, ища переменную.
-
попытаться найти переменную undefined из внутренних замыканий будет самой дорогой, потому что по определению переменная будет первым поиском в родительской области до тех пор, пока глобальная область действия, и не найдя ее, заставит двигатель наконец, достичь глобального масштаба. Это правда? являются ли механизмы, оптимизирующие этот вид поиска?
В конце... Я знаю, что я не хочу использовать свой код в качестве второго примера в основном потому, что он сделает код более трудным для чтения, и я любезно согласен с минимальным размером финального вывода, используя первый подход. Мой вопрос мотивирован любопытством и попытаться понять немного лучше эту действительно приятную особенность javascript.
В соответствии с этим тестом...
http://jsperf.com/closures-js
Кажется, второй подход быстрее. Но это проявляется только при повторении безумного количества раз... Прямо сейчас мой код не выполняет такое количество итераций... но, вероятно, потребляет больше памяти из-за моего способа кодирования...
update: он указал мне, что этот вопрос слишком велик. Извините, я попытаюсь сломать мелкие детали. Этот вопрос был мотивирован в основном любопытством, как я сказал, производительность кажется незначительной даже на мобильных устройствах. Благодарим вас за все ваши отзывы.
Ответы
Ответ 1
Я думаю, что это преждевременная оптимизация. Вы уже знаете, что производительность не является проблемой в большинстве случаев. Даже в плотных петлях производительность не ухудшает это плохо. Пусть JavaScript-движок оптимизирует это самостоятельно, поскольку Chrome начал делать, удалив ненужные переменные из закрытий.
Важно одно: не делайте код сложнее читать с ненужной оптимизацией. В вашем примере требуется еще немного кода, что затрудняет разработку. В некоторых случаях мы вынуждены делать код более трудным для чтения, потому что мы знаем, что определенная часть приложения больше связана с памятью/производительностью, но только в этот момент мы должны это сделать.
Если вы добавите точку останова в следующий код (в Chrome), вы увидите, что переменная world
была оптимизирована из закрытия, посмотрите на "Closure" node в разделе "Переменные области" http://jsfiddle.net/4J6JP/1/
.
(function(){
var hello = "hello", world="world";
setTimeout(function(){
debugger;
console.log(hello);
});
})()
Обратите внимание, что если вы добавите eval в эту внутреннюю функцию, тогда все ставки будут отключены, а закрытие невозможно оптимизировать.
Ответ 2
Существует стоимость производительности при создании областей и контекстов функций, добавление лишней детской области имеет стоимость, однако в этом случае она пренебрежимо мала. Итерирование только тех объектов или переменных, которые вам нужны во время работы, будет быстрее, чем преодоление лексической области и поиск там.
Хорошая статья о функциях вложенности приведена здесь: http://code.tutsplus.com/tutorials/stop-nesting-functions-but-not-all-of-them--net-22315
Если я могу получить ссылку и назначить ее var внутри лексической области, я сделаю это, но нужно быть осторожным, потому что даже после того, как родительская функция будет уничтожена, возвращаемая функция или объект могут получить доступ к этим ссылкам для "long.lib.format" с сокращенным "форматом", потому что они были объявлены внутри одной и той же лексической области, если вы больше не используете ссылку, вам нужно будет уничтожить все пути в этой области. Это печально, но в это время я понимаю, что мы не можем удалить "ссылку" в этом случае "var format", поскольку тот же самый lib ссылается в другом месте, поэтому "delete format" не разрешен, однако вы можете удалить участников на объекте /lib он ссылается. Вы можете var локальный объект для хранения ссылок и удаления их, когда они больше не нужны, а затем уничтожить этот объект, не теряя других членов лексической области или области содержимого
Обновление: недавно выяснилось, что использование delete в javascript может быть медленным, скорее назначьте члену {}, когда он больше не используется.