Влияет ли использование анонимных функций на производительность?

StackOverflow https://stackoverflow.com/questions/80802

Вопрос

Мне было интересно, есть ли разница в производительности между использованием именованных функций и анонимных функций в Javascript?

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;
}

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

Это было полезно?

Решение

Проблема производительности здесь заключается в стоимости создания нового объекта function на каждой итерации цикла, а не в том факте, что вы используете анонимную функцию:

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:1983 мс

Похоже, что движок JS не распознает, что это одна и та же функция в Test2, и компилирует ее каждый раз.

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

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

Это очень полезная функция в конкретных случаях, но ее не следует использовать во многих ситуациях.

Я бы не ожидал большой разницы, но если она и есть, то, скорее всего, она будет зависеть от скриптового движка или браузера.

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

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

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

Предполагая, что вы пишете хорошо спроектированный код, тогда вопросы скорости должны быть ответственностью тех, кто пишет интерпретаторы / компиляторы.

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

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

Используя тестовый фреймворк, аналогичный вашему:


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) { ... }.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top