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

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

Что-то, что всегда казалось мне странным, это то, что, несмотря на кошмар читаемости, который является гнездом JavaScript вложенные обратные вызовы, одна вещь, которую я очень редко вижу во многих примерах и учебниках, - это использование предопределенных именованных функций в качестве аргументов обратного вызова. Я программист Java днем, и отбрасывание стереотипных jabs о названиях Enterprise-y для единиц кода одна из вещей, которые мне нравятся в работе на языке с сильным выбором характерных IDE, заключается в том, что использование значимых, если длинных, имен может сделать намерение и смысл кода намного яснее не делая более трудным быть продуктивным. Так почему бы не использовать тот же подход при написании кода JavaScript?

плюсы:

  • гибкость. Асинхронная функция с параметром обратного вызова может быть достигнута одним из многих различных путей кода, и его можно было бы поторопить, чтобы написать именованную функцию для учета каждого возможного крайнего случая.
  • скорость. Это сильно влияет на менталитет хакеров. Прикрепите к нему болты, пока он не заработает.
  • все остальные делают это
  • меньшие размеры файлов, даже если это тривиально, но каждый бит считается в интернете.
  • проще АСТ? Я бы предположил, что анонимные функции генерируются во время выполнения, и поэтому JIT не буду возиться с сопоставлением имени с инструкциями, но я просто предполагаю на данный момент.
  • быстрее посылать? Насчет этого тоже не уверен. Опять гадание.

плюсы:

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

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

Итак, есть ли какие-либо технические причины или gotchas, о которых я не знаю, что делает эту практику настолько распространенной по какой-то причине?

5 ответов


Я использую анонимные функции по трем причинам:

  1. Если имя не требуется, потому что функция вызывается только в одном месте, зачем добавлять имя в любое пространство имен, в котором вы находитесь.
  2. анонимные функции объявляются встроенными, а встроенные функции имеют преимущества в том, что они могут обращаться к переменным в родительских областях. Да, вы можете поместить имя в анонимную функцию, но это обычно бессмысленно, если она объявлена встроенной. Так inline имеет значительное преимущество, и если вы делаете inline, есть мало причин, чтобы поставить имя на нем.
  3. код кажется более автономным и читаемым, когда обработчики определены прямо внутри кода, который их вызывает. Вы можете читать код почти последовательно, а не искать функцию с этим именем.

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

Я думаю, я бы добавил, что если обратный вызов начинает получать более 15-20 строк, и ему не нужен прямой доступ к переменным в родительской области, я бы соблазнился дать ему имя и разбить его на собственную именованную функцию, объявленную в другом месте. Здесь определенно есть точка читаемости, где нетривиальная функция, которая gets long просто более ремонтопригоден, если он помещен в собственный именованный блок. Но большинство обратных вызовов, которые я получаю, не так длинны, и я нахожу более читаемым, чтобы держать их в строке.


Я предпочитаю сам именованные функции, но для меня это сводится к одному вопросу:

буду ли я использовать эту функцию в другом месте?

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

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

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

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

предположим, обе эти функции делают то же самое:

function addTimes(time1, time2)
{
    // return time1 + time2;
}

function addTwoTimesIn24HourFormat(time1, time2)
{
    // return time1 + time2;
}

второй говорит вам точно, что он делает в имени. Первый более неоднозначен. Тем не менее, есть 17 символов разницы в названии. Скажем, функция вызывается 8 раз на протяжении всего кода, это 153 дополнительный байт ваш код не нужно. Не колоссально, но если это привычка, экстраполяция на 10 или даже 100 функций легко будет означать несколько КБ разница в загрузке.

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


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

var x = function(){ alert('hi'); },

indexOfHandyMethods = {
   hi: function(){ alert('hi'); },
   high: function(){
       buyPotatoChips();
       playBobMarley();
   }
};

someObject.someEventListenerHandlerAssigner( function(e){
    if(e.doIt === true){ doStuff(e.someId); }
} );

(function namedButAnon(){ alert('name visible internally only'); })()

плюсы:

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

  • code разборчивость win: в Примере объектного литерала с функциями anon, назначенными в качестве методов, было бы глупо добавлять больше мест для охоты и клюв для логики в вашем коде, когда весь смысл этого объектного литерала заключается в том, чтобы плюхнуть некоторые связанные функции в том же удобном месте. Однако при объявлении общедоступных методов в конструкторе я склонен определять помеченные функции inline, а затем назначать в качестве ссылок на это.sameFuncName. Это позволяет я использую те же методы внутренне без "этого"."cruft и делает порядок определения безразличным, когда они называют друг друга.

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

  • Я согласен с встроенными обратными вызовами при установке коротких обработчиков событий. Глупо иметь чтобы охотиться за функцией 1-5 строк, особенно с JS и функцией подъема, определения могут оказаться в любом месте, даже не в одном файле. Это может произойти случайно, ничего не сломав, и нет,вы не всегда контролируете это. События всегда приводят к запуску функции обратного вызова. Нет причин добавлять больше ссылок в цепочку имен, которые вам нужно сканировать, чтобы просто перепроектировать простые обработчики событий в большой кодовой базе, и проблема трассировки стека может решается путем абстрагирования триггеров событий в методы, которые регистрируют полезную информацию при включенном режиме отладки и запускают триггеры. На самом деле я начинаю создавать целые интерфейсы таким образом.

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

плюсы:

  • функции Анон не могут принять преимущество поднимать функции. Это большая разница. Я, как правило, использую тяжелое преимущество подъема, чтобы определить мои собственные явно названные функции и конструкторы объектов снизу и добраться до определения объекта и материала типа основного цикла прямо вверху. Я нахожу, что это облегчает чтение кода, когда вы называете свои vars и получаете широкое представление о том, что происходит раньше ctrl-Fing для деталей только тогда, когда они важны для вас. Подъем также может быть огромным преимуществом в сильно управляемых событиями интерфейсах, где навязывание строгого порядка того, что доступно, когда может укусить вас в задницу. Подъем имеет свои собственные предостережения (например, круговой ссылочный потенциал), но это очень полезный инструмент для организации и разборчивости кода при правильном использовании.

  • Удобочитаемость/Отладки. Абсолютно они слишком часто используются, и это может сделать отладку и удобочитаемость кода перебранка. Например, кодовые базы, которые сильно зависят от JQ, могут быть серьезной питой для чтения и отладки, если вы не инкапсулируете почти неизбежные тяжелые и массово перегруженные args $ soup разумным способом. Метод наведения jQuery, например, является классическим примером чрезмерного использования функций anon, когда вы бросаете в него две функции anon, поскольку для первого таймера легко предположить, что это стандартный метод назначения прослушивателя событий, а не один метод, перегруженный для назначения обработчиков для одного или двух события. $(this).hover(onMouseOver, onMouseOut) намного яснее, чем две функции anon.


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

(function recursion(iteration){
    if (iteration > 0) {
      console.log(iteration);
      recursion(--iteration);
    } else {
      console.log('done');
    }
})(20);

console.log('recursion defined? ' + (typeof recursion === 'function'));

http://jsfiddle.net/Yq2WD/

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

Привет, меня зовут Джейсон или Привет, меня зовут ???? вы выбираете.


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

Anon функции не легко упоминаются в гуманоидных разговорах о коде, среди команды. Например, - Джо, не мог бы ты объяснить, что делает алгоритм внутри этой функции? ... Который? 17-я анонимная функция в функции fooApp. ... Нет, только не это! 17-й!"

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

опасения по поводу загрязнения глобального пространства имен допустимы, но легко устраняются, называя ваши функции узлами в вашем собственном корневом объекте, например "myFooApp.happyFunc = функция ( ... ) { ... }; ".

функции, доступные в глобальном пространстве имен или как узлы в корневом объекте, как указано выше, могут вызываться из отладчика непосредственно во время разработки и отладки. Е. Г., в командной строке консоли делать "myFooApp.happyFunc (42)". Это чрезвычайно мощная способность, которая не существует (изначально) на компилируемых языках программирования. Попробуй Анон кнопку func.

функс Анон можно сделать более читаемым мимо назначение их var, а затем передача var в качестве обратного вызова (вместо вставки). Например.: var funky = функция ( ... ) { ... }; jQuery ('#otis').нажмите кнопку(фанки);

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