Функции вложенности и производительность в javascript?

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

Допустим, у меня есть функция:

function calculateStuff() {
    function helper() {
    // helper does things
    }
    // calculateStuff does things
    helper();
}

helper - это частная функция, которая используется только внутри calculateStuff. Вот почему я хотел инкапсулировать это внутри calculateStuff.

Это хуже с точки зрения производительности, чем делать:

function helper() {

}

function calculateStuff() {
    helper();
}

Обратите внимание, что во втором случае я предоставляю помощник своей области видимости.


person user1413969    schedule 20.10.2015    source источник
comment
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что показатели производительности лучше всего обрабатываются jsperf и имеют короткий срок хранения по мере развития браузеров.   -  person Mathletics    schedule 21.10.2015
comment
Не спрашивайте других о производительности. Если сомневаетесь, измерьте.   -  person Bergi    schedule 21.10.2015


Ответы (2)


С вашим первым кодом при каждом вызове calculateStuff будет создаваться новая копия helper.

С вашим вторым кодом все вызовы будут использовать один и тот же helper, но это будет загрязнять внешнюю область видимости.

Если вы хотите повторно использовать helper, не загрязняя внешнюю область видимости, вы можете использовать IIFE:

var calculateStuff = (function () {
  function helper() {
    // helper does things
  }
  return function() {
    // calculateStuff does things
    helper();
  }
})();
person Oriol    schedule 20.10.2015
comment
Вы по-прежнему загрязняете внешнюю область видимости, хотя это не будет глобальной областью действия. - person MinusFour; 21.10.2015
comment
@MinusFour чем именно загрязняет его? - person zerkms; 21.10.2015
comment
@MinusFour Он создает промежуточную область видимости и загрязняет ее вместо внешней. - person Oriol; 21.10.2015
comment
Я просто говорю, что промежуточная область видимости также может считаться внешней областью (по крайней мере, из контекста анонимной функции). - person MinusFour; 21.10.2015
comment
@MinusFour, что не имеет смысла. - person zerkms; 21.10.2015

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

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

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


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

Вложенные функции: 136 000 000 вызовов в секунду

Плоские функции: 1 035 000 000 кал в секунду

Версия IIFE Ориола: 220 000 000 кал в секунду

Очевидно, что плоские функции намного быстрее, чем любая из альтернативных версий. Однако подумайте о величине этих чисел - даже «медленная» версия добавляет ко времени выполнения всего 0,007 микросекунды. Если вы выполните какие-либо вычисления или манипуляции с DOM в этой функции, это полностью уменьшит накладные расходы вложенной функции.

person Mark Bessey    schedule 20.10.2015
comment
для создания нового контекста закрытия для помощника каждый раз, когда вызывается calculateStuff (потому что он может ссылаться на переменные из охватывающей области). --- так что если он не относится ни к каким свободным переменным - такого влияния нет вообще? Если это то, что вы пытаетесь выразить, то это неправильно. - person zerkms; 21.10.2015
comment
@zerkms: Да, если он не ссылается на свободные переменные, и особенно если он не экспортируется из этой области, функция может и будет встроена. Как это не так? - person Bergi; 21.10.2015
comment
@Bergi в стандарте этого не требует. И двигатели этого не гарантируют. Как это не так? --- нет ничего, что ГАРАНТИРУЕТ создание функции бесплатно. - person zerkms; 21.10.2015
comment
@zerkms: Ни я, ни Марк этого не говорили. Конечно, стандарт ничего не требует о производительности (кроме карт и наборов ES5), но Марк прав в том, что большинство JIT-компиляторов (т.е. фактических реализаций) могут пропустить создание объекта функции или даже закрытие контекста в куче. - person Bergi; 21.10.2015
comment
@Bergi прав, но это означает, что привязка лексической области видимости - единственные накладные расходы. А создание функции в целом совсем недешево. Итак, я хочу сказать, что ответ подчеркивает привязку области видимости и упускает из виду важную часть создания объекта функции. - person zerkms; 21.10.2015
comment
@zerkms: В любом случае, ответ должен быть одобрен за Если есть сомнения, профилируйте его самостоятельно :-) - person Bergi; 21.10.2015
comment
Я сделал этот тест, только что на Firefox 56 и Chrome 62, оба показали, что вложенный - самый быстрый. Вложенный, плоский, IIFE - (FF - 706, 655, 415) (Chrome - 642, 118, 117). Я не знаю, изменилось ли это с тех пор, но похоже, что вложенные функции на самом деле лучше для производительности сейчас, не говоря уже о предоставлении лучшей лексической области видимости вместо плоских, бессмысленных функций, которые некоторые люди считают хорошей практикой и выступают против вложенности . - person norbidrak; 03.06.2020
comment
Используя тесты jsperf, которые я опубликовал выше, я получаю практически идентичные результаты для всех трех методов с Chrome 83 и Firefox 77, а Nested все еще медленнее для Safari 13. В качестве примечания, результаты производительности, которые я получаю для Safari, теперь в 10 раз хуже, чем Chrome на том же компьютере, хотя это не соответствует реальной производительности реального кода. Сравнительный анализ может быть непростым делом ... - person Mark Bessey; 03.06.2020