Влияет ли использование анонимных функций на производительность? - PullRequest
81 голосов
/ 17 сентября 2008

Мне было интересно, есть ли разница в производительности между использованием именованных функций и анонимных функций в 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;
}

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

Ответы [ 12 ]

81 голосов
/ 17 сентября 2008

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

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

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

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

21 голосов
/ 17 сентября 2008

Вот мой тестовый код:

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, и компилирует ее каждый раз.

2 голосов
/ 17 сентября 2008

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

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

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

1 голос
/ 25 ноября 2015

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

http://jsperf.com/function-context-benchmark

В Chrome операция будет быстрее, если мы объявим функцию снаружи, но в Firefox все наоборот.

В другом примере мы видим, что если внутренняя функция не является чистой функцией, она будет иметь недостаточную производительность также в Firefox: http://jsperf.com/function-context-benchmark-3

1 голос
/ 17 сентября 2008

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

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

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

1 голос
/ 17 сентября 2008

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

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

0 голосов
/ 17 июня 2017

Как указано в комментариях к @nickf, ответ: Ответ на

Создает ли функцию один раз быстрее, чем миллион раз

это просто да. Но, как показывает его опыт JS, он не медленнее в миллион раз, показывая, что со временем он становится быстрее.

Более интересный вопрос для меня:

Как повторное создание + запуск сравнивается с созданием один раз + повторное выполнение .

Если функция выполняет сложное вычисление, время создания объекта функции, скорее всего, незначительно. Но как насчет чрезмерной create в случаях, когда run работает быстро? Например:

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

0 голосов
/ 17 сентября 2008

@ nickf

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

Суть в том, что здесь есть путаница между именованными / анонимными функциями и сценарием выполнения + компиляции в итерации. Как я проиллюстрировал, разница между anon + named незначительна сама по себе - я говорю, что ошибочный вариант использования.

Это кажется очевидным для меня, но если нет, то я думаю, что лучший совет - "не делай глупостей" (из которых одно постоянное смещение блоков + создание объектов в этом сценарии использования), и если ты не уверен , протестируй!

0 голосов
/ 17 сентября 2008

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

Здесь действительно хорошая статья об оптимизации javascript и анонимных функций:

http://dev.opera.com/articles/view/efficient-javascript/?page=2

0 голосов
/ 17 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...