Влияет ли использование анонимных функций на производительность?
мне было интересно, есть ли разница в производительности между использованием именованных функций и анонимных функций в Javascript?
for (var i = 0; i < 1000; ++i) {
myObjects[i].onMyEvent = function() {
// do something
};
}
vs
function myEventHandler() {
// do something
}
for (var i = 0; i < 1000; ++i) {
myObjects[i].onMyEvent = myEventHandler;
}
первый более аккуратный, так как он не загромождает ваш код редко используемыми функциями, но имеет ли значение, что вы повторно объявляете эту функцию несколько раз?
12 ответов:
проблема производительности здесь заключается в стоимости создания нового объекта функции на каждой итерации цикла, а не в том, что вы используете анонимную функцию:
for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = function() { // do something }; }вы создаете тысячу различных объектов функций, даже если они имеют один и тот же код и не привязаны к лексической области (закрытие). С другой стороны, следующее кажется более быстрым, потому что оно просто присваивает то же самое ссылка функции на элементы массива на протяжении всего цикла:
function myEventHandler() { // do something } for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = myEventHandler; }если вы должны были создать анонимную функцию перед входом в цикл, то только назначьте ссылки на нее элементам массива, находясь внутри цикла, вы обнаружите, что нет никакой производительности или семантической разницы по сравнению с версией именованной функции:
var handler = function() { // do something }; for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = handler; }короче говоря, нет заметной стоимости производительности для использования анонимных по именованным функциям.
кроме того, может показаться, что нет никакой разницы между:
function myEventHandler() { /* ... */ }и:
var myEventHandler = function() { /* ... */ }первое-это объявление функции в то время как последний является присвоением переменной анонимной функции. Хотя они могут иметь одинаковый эффект, JavaScript относится к ним немного по-другому. Чтобы понять разницу, я рекомендую прочитать:"двусмысленность объявления функции JavaScript".
фактическое время выполнения любого подхода, в значительной степени будет продиктовано реализацией браузера компилятора и среды выполнения. Для полного сравнения производительности современного браузера, посетите сайт JS Perf
вот мой тестовый код:
var dummyVar; function test1() { for (var i = 0; i < 1000000; ++i) { dummyVar = myFunc; } } function test2() { for (var i = 0; i < 1000000; ++i) { dummyVar = function() { var x = 0; x++; }; } } function myFunc() { var x = 0; x++; } document.onclick = function() { var start = new Date(); test1(); var mid = new Date(); test2(); var end = new Date(); alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid)); }результаты:
Тест 1: 142мс Тест 2: 1983msпохоже, что движок JS не распознает, что это одна и та же функция в Test2 и компилирует ее каждый раз.
в качестве общего принципа проектирования вы должны избегать многократного использования одного и того же кода. Вместо этого вы должны поднять общий код в функцию и выполнить эту (общую, хорошо протестированную, легко модифицируемую) функцию из нескольких мест.
Если (в отличие от того, что вы выводите из своего вопроса) Вы объявляете внутреннюю функцию один раз и используете этот код один раз (и не имеете ничего другого идентичного в своей программе), то anonomous function наверное (вот думаю folks) обрабатывается компилятором так же, как и обычная именованная функция.
очень полезная функция в конкретных случаях, но не должна использоваться во многих ситуациях.
Я бы не ожидал большой разницы, но если она есть, она, вероятно, будет отличаться от скриптового движка или браузера.
Если вы считаете, что код легче Грок, производительность не является проблемой, если вы не ожидаете, чтобы вызвать функцию миллионы раз.
анонимные объекты быстрее, чем именованные объекты. Но вызов большего количества функций стоит дороже, и до такой степени, что затмевает любую экономию, которую вы можете получить от использования анонимных функций. Каждая вызванная функция добавляет к стеку вызовов, который вводит небольшое, но нетривиальное количество накладных расходов.
но если вы не пишете процедуры шифрования / дешифрования или что-то подобное чувствительное к производительности, как многие другие отметили, всегда лучше оптимизировать для элегантного, легко читаемый код над быстрым кодом.
предполагая, что вы пишете хорошо спроектированный код, то вопросы скорости должны быть ответственностью тех, кто пишет интерпретаторы/компиляторы.
где мы можем иметь влияние на производительность в операции объявления функций. Вот эталон объявления функций внутри контекста другой функции или снаружи:
http://jsperf.com/function-context-benchmark
в Chrome работа быстрее, если мы объявим функцию снаружи, но в Firefox все наоборот.
в другом примере мы видим, что если внутренняя функция не является чистой функцией, он будет иметь недостаток производительности также В Firefox: http://jsperf.com/function-context-benchmark-3
что определенно сделает ваш цикл быстрее в различных браузерах, особенно в браузерах IE, зацикливается следующим образом:
for (var i = 0, iLength = imgs.length; i < iLength; i++) { // do something }вы ввели произвольную 1000 в условие цикла, но вы получаете мой дрейф, если хотите пройти через все элементы в массиве.
ссылка почти всегда будет медленнее, чем то, на что она ссылается. Подумайте об этом таким образом - допустим, вы хотите напечатать результат добавления 1 + 1. Что имеет больше смысла:
alert(1 + 1);или
a = 1; b = 1; alert(a + b);Я понимаю, что это действительно упрощенный способ взглянуть на это, но это иллюстративно, не так ли? Использовать ссылку, только если она будет использоваться несколько раз, например, какой из этих примеров делает больше смысл:
$(a.button1).click(function(){alert('you clicked ' + this);}); $(a.button2).click(function(){alert('you clicked ' + this);});или
function buttonClickHandler(){alert('you clicked ' + this);} $(a.button1).click(buttonClickHandler); $(a.button2).click(buttonClickHandler);второй-лучшая практика, даже если у него больше строк. Надеюсь, все это полезно. (и синтаксис jquery никого не сбил)
@nickf
(жаль, что у меня не было репутации, чтобы просто прокомментировать, но я только что нашел этот сайт)
Я хочу сказать, что здесь есть путаница между именованными/анонимными функциями и вариантом использования выполнения + компиляции в итерации. Как я проиллюстрировал, разница между Anon + named незначительна сама по себе-я говорю, что это случай использования, который неисправен.
Это кажется мне очевидным, но если нет, я думаю, что лучший совет - "не делайте глупых вещей" (из которых постоянный сдвиг блока + создание объекта этого варианта использования-один), и если вы не уверены, проверьте!
да! Анонимные функции работают быстрее, чем обычные. Возможно, если скорость имеет первостепенное значение... более важно, чем повторное использование кода, а затем рассмотреть возможность использования анонимных функций.
здесь есть очень хорошая статья об оптимизации javascript и анонимных функций:
http://dev.opera.com/articles/view/efficient-javascript/?page=2
@nickf
Это довольно глупый тест, хотя, вы сравниваете выполнение и сборник время там, которое, очевидно, будет стоить метод 1 (компилирует N раз, в зависимости от двигателя JS) с методом 2 (компилирует один раз). Я не могу представить себе разработчика JS, который бы прошел свой испытательный срок, написав код таким образом.
гораздо более реалистичным подходом является анонимное назначение, как на самом деле вы используете для вашего документа.метод onclick больше нравится следующее, что на самом деле мягко благоприятствует методу Анона.
использование аналогичной тестовой структуры для вашего:
function test(m) { for (var i = 0; i < 1000000; ++i) { m(); } } function named() {var x = 0; x++;} var test1 = named; var test2 = function() {var x = 0; x++;} document.onclick = function() { var start = new Date(); test(test1); var mid = new Date(); test(test2); var end = new Date(); alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms"); }
Как указано в комментариях к @nickf Ответ: Ответ на
создает функцию один раз быстрее, чем создание его в миллион раз
просто да. Но, как показывает его JS perf, он не замедляется в миллион раз, показывая, что он действительно становится быстрее с течением времени.
более интересный вопрос для меня это:
Как это повторяется создать + запустить сравнить, чтобы создать один раз + повторяется выполнить.
Если функция выполняет сложное вычисление, то время для создания объекта функции, скорее всего, незначительно. Но как насчет над головой создать в случаях, когда выполнить быстро? Например:
// Variant 1: create once function adder(a, b) { return a + b; } for (var i = 0; i < 100000; ++i) { var x = adder(412, 123); } // Variant 2: repeated creation via function statement for (var i = 0; i < 100000; ++i) { function adder(a, b) { return a + b; } var x = adder(412, 123); } // Variant 3: repeated creation via function expression for (var i = 0; i < 100000; ++i) { var x = (function(a, b) { return a + b; })(412, 123); }этой JS Perf показывает, что создание функции только один раз быстрее, как и ожидалось. Однако даже при очень быстрой операции, такой как простое добавление, накладные расходы на создание функция многократно составляет всего несколько процентов.
разница, вероятно, становится значительной только в тех случаях, когда создание объекта функции является сложным, сохраняя при этом незначительное время выполнения, например, если все тело функции завернуто в
if (unlikelyCondition) { ... }.
Comments