Зачем использовать выражения именованных функций?

У нас есть два разных способа сделать выражение функции в JavaScript:

Выражение именованной функции (NFE):

var boo = function boo () {
  alert(1);
};

Выражение анонимной функции:

var boo = function () {
  alert(1);
};

И оба они могут быть вызваны с boo();, Я действительно не понимаю, почему / когда я должен использовать анонимные функции и когда я должен использовать выражения именованных функций. Какая разница между ними?

5 ответов

Решение

В случае выражения анонимной функции, функция является анонимной - буквально, она не имеет имени. Переменная, которой вы ее назначаете, имеет имя, а функция - нет. (Обновление: это было верно в ES5. Начиная с ES2015 [aka ES6], часто функция, созданная с помощью анонимного выражения, получает истинное имя, читайте дальше...)

Имена полезны. Имена можно увидеть в следах стеков, стеках вызовов, списках точек останова и т. Д. Имена - это хорошая вещь ™.

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

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

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

var boo = function() { /*...*/ };

(или это может быть let или же const скорее, чем var ) или

var obj = {
    boo: function() { /*...*/ }
};

или же

doSomething({
    boo: function() { /*...*/ }
});

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

Существует важное и намеренное исключение: назначение свойства существующему объекту:

obj.boo = function() { /*...*/ }; // <== Does not get a name

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

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

Например, рассмотрим этот код:

setTimeout(function sayMoo() {
    alert('MOO');
    setTimeout(sayMoo, 1000);
}, 1000);

Было бы невозможно написать этот код так чисто, если бы выражение функции передавалось setTimeout были анонимными; нам нужно было бы присвоить его переменной вместо setTimeout вызов. Таким образом, с выражением именованной функции немного короче и аккуратнее.

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

setTimeout(function () {
    alert('MOO');
    setTimeout(arguments.callee, 1000);
}, 1000);

... но arguments.callee устарела и полностью запрещена в строгом режиме ES5. Следовательно, MDN советует:

Избегать использования arguments.callee() либо давая выражения функции имени или использовать объявление функции, где функция должна вызывать себя.

(акцент мой)

Вы всегда должны использовать функциональные выражения NAMED.

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

2. Анонимные функции не помогают при отладке, так как вы не видите название функции, которая вызывает проблемы.

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

var foo = function bar() {
 //some code...
};
foo();
bar(); // Error!

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

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

Он будет доступен только внутри функции (кроме IE8-).

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

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

var f = function sayHi(name) {
  alert( sayHi ); // Inside the function you can see the function code
};

alert( sayHi ); // (Error: undefined variable 'sayHi')

Кроме того, имя NFE (выражение именованной функции) не может быть перезаписано:

var test = function sayHi(name) {
  sayHi = "тест"; // try to redefine
  alert( sayHi ); // function... (redefinition is unsuccessful )
};

test();

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

Другие вопросы по тегам