JavaScript: встроенные функции против предопределенных функций - PullRequest
44 голосов
/ 29 марта 2010

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

т.е. что лучше:

(function() {
  setTimeout(function() { /*some code here*/ }, 5);
})();

против

(function() {
  function invokeMe() {
    /*code*/
  }
  setTimeout(invokeMe, 5);
})();


Странный вопрос, но мы почти сражаемся в команде по этому поводу.

Ответы [ 13 ]

52 голосов
/ 07 января 2012

Именованные функции

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

Используется выражение функции :

setTimeout(function doSomethingLater() { alert('In a named function.'); }, 5);

и используется оператор функции :

function doSomethingLater() { alert('In a named function.'); }
setTimeout(doSomethingLater, 5);

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

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

Примечание: T.J. указывает на то, что IE неправильно обрабатывает выражения именованных функций (см .: http://kangax.github.com/nfe/#jscript-bugs), и это важно отметить, я просто пытаюсь сделать вывод о терминологии.

Что вы должны использовать?

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

Утечки памяти

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

(function outerFunction() {
    var A = 'some variable';

   doStuff();
})();

В приведенном выше коде, когда "externalFunction" завершает работу, "A" выходит из области видимости и может быть подвергнута сборке мусора, освобождая эту память.

Что если мы добавим туда функцию?

(function outerFunction() {
    var A = 'some variable';

   setTimeout(function(){ alert('I have access to A whether I use it or not'); }, 5);
})();

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

Что если мы передадим эту функцию чему-то другому, кроме setTimeout?

(function outerFunction() {
    var A = 'some variable';

   doStuff(function(){ alert('I have access to A whether I use it or not'); });
})();

function doStuff(fn) {
    someElement.onclick = fn;
}

Теперь выражение функции, которое мы передаем «doStuff», имеет доступ к «A», и даже после завершения «externalFunction» «A» будет оставаться в памяти до тех пор, пока есть ссылка на переданную нами функцию в doStuff . В этом случае мы создаем ссылку на эту функцию (в качестве обработчика события), и поэтому «A» будет оставаться в памяти до тех пор, пока этот обработчик события не будет очищен. (например, кто-то звонит someElement.onclick = null)

Теперь посмотрим, что происходит, когда мы используем оператор функции:

(function outerFunction() {
    var A = 'some variable';

    function myFunction() { alert('I have also have access to A'); };
    doStuff(myFunction);
})();

Та же проблема! «myFunction» будет очищен, только если «doStuff» не содержит ссылку на него, а «A» будет очищен только после очистки «myFunction». Неважно, использовали ли мы утверждение или выражение; важно, если ссылка на эту функцию создается в doStuff!

11 голосов
/ 29 марта 2010

Существует одна существенная разница между двумя: у последней есть имя.

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

(function(){
  function invokeMe() {
    /*code*/
  }
  setTimeout(invokeMe, 5);
})();

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

Обратите внимание, что в соответствии со спецификацией существует третий вариант: у вас может быть встроенная функция, которая также имеет имя:

(function(){
  setTimeout(function invokeMe(){ /*some code here*/ }, 5);
})();

Проблема, однако, заключается в том, что каждая версия интерпретатора JavaScript от Microsoft («JScript»), включая (удивительно) версию в IE9, неправильно обрабатывает именованное выражение функции и создает две совершенно разные функции в разное время. ( Proof , попробуйте его в IE9 или более ранней версии, а также практически в любом другом браузере.) IE ошибается двумя способами: 1. Создает два отдельных функциональных объекта и 2. Как следствие одного из них он «сливает» символ имени в прилагаемую область выражения (в явном нарушении раздела 13 спецификации). Подробности здесь: Двойной дубль

5 голосов
/ 29 марта 2010

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

Я лично использую выражения функций (первый способ) для обработчиков setTimeout.

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

3 голосов
/ 29 марта 2010

Я предлагаю провести полный поединок между противоборствующими членами команды для урегулирования таких споров.

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

Любой аргумент против любого стиля - чистый байкшеддинг , имхо.

2 голосов
/ 29 марта 2010

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

1 голос
/ 13 ноября 2016

Предопределенные именованные функции могут уменьшить проблему ада обратного вызова JavaScript, которая упоминается в http://callbackhell.com/

1 голос
/ 11 октября 2015

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

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

//an example where you wold prefer to use an anonymous function.
//you can assign this (anonymous) function to a variable, so you get your "name" back.
function someFn(){
    if(condition){
        //the variable declaration has been hoisted, 
        //but the function is created at this point, and only if necessary.
        var process = function(value){/* */};
        switch(condition2){
            case 1: process(valueFor1); break;
            case 2: process(valueFor2); break;
            /* ... */
        }
    }
}

function someFn(){
    var process;
    if(condition){
        process = function(value){ /* A */ }
    }else{
        process = function(value){ /* B */ }
    }

    //beware, depending on your code, "process" may be undefined or not a function
    process(someValue);
}


//an example where you would prefer (/ utilize) the hoisting.
function someFn(){
    /* some code */
    while(condition){
        //some might want to keep the function definition near the code where it is used,
        //but unlike an anonymous function or a lambda-expression this process-function 
        //is created only once per function-call, not once per iteration.
        function process(value, index){ /* ... */ }
        /* ... */
        process(value, index)
    }
}

так, как правило:

  • внутри цикла не должно быть анонимной функции или лямбда-выражения

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

  • если вы знаете свой бизнес (JavaScript), вы знаете, когда игнорировать этот совет

1 голос
/ 03 апреля 2014

Разве мы не можем просто ладить?

(function(){
  setTimeout( (function InvokeMe(){ /*some code here*/ }), 5);
})();

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

1 голос
/ 29 марта 2010

Я думаю, что единственное отличие в таком коде состоит в том, что со вторым фрагментом кода вы можете повторно вызывать ту же функцию (иногда с помощью «функций таймера» это полезно):

(function(){
  function invokeMe() {
    if(..) setTimeout(invokeMe, 5);
  }
  setTimeout(invokeMe, 5);
})();
0 голосов
/ 22 июля 2011

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

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

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

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

Именованные функции также абсолютно необходимы, если вы когда-нибудь попадете в Test Driven Development.

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