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

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

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

Ответ 1

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

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

Ответ 2

Здесь мой тестовый код:

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

Ответ 7

Что определенно сделает ваш цикл более быстрым в разных браузерах, особенно в IE-браузерах, будет циклическим:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

Вы ввели произвольное 1000 в условие цикла, но вы получите мой дрейф, если хотите просмотреть все элементы в массиве.

Ответ 8

@nickf

Это довольно отвратительный тест, хотя вы сравниваете время выполнения и компиляции там, которое, очевидно, будет стоить метод 1 (компилирует N раз, JS-движок зависит) с методом 2 (компиляция один раз). Я не могу представить себе разработчика JS, который таким образом передавал бы код для пробации.

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

Ответ 9

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

Ответ 10

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

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

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

Ответ 11

@nickf

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

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

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

Ответ 12

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

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

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

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

Как повторяется 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) { ... }.