Istnieją dwa różne sposoby wykonywania wyrażeń funkcyjnych w JavaScript:
Nazwane wyrażenie funkcyjne (NFE) :
var boo = function boo () {
alert(1);
};
Anonimowe wyrażenie funkcyjne :
var boo = function () {
alert(1);
};
I oba z nich można wywołać za pomocą boo();
. Naprawdę nie rozumiem, dlaczego / kiedy powinienem używać funkcji anonimowych i kiedy powinienem używać nazwanych wyrażeń funkcyjnych. Jaka jest między nimi różnica?
javascript
function
anonymous-function
function-expression
Afshin Mehrabani
źródło
źródło
Odpowiedzi:
W przypadku anonimowego wyrażenia funkcyjnego funkcja jest anonimowa - dosłownie nie ma nazwy. Zmienna, do której jest przypisywana, ma nazwę, ale funkcja nie. (Aktualizacja: tak było w ES5. Od ES2015 [aka ES6] często funkcja utworzona za pomocą anonimowego wyrażenia otrzymuje prawdziwą nazwę [ale nie automatyczny identyfikator], czytaj dalej ...)
Nazwy są przydatne. Nazwy można zobaczyć w śladach stosu, stosach wywołań, listach punktów przerwania, itp. Nazwy są dobrą rzeczą ™.
(Kiedyś trzeba było uważać na nazwane wyrażenia funkcyjne w starszych wersjach IE [IE8 i poniżej], ponieważ omyłkowo utworzyły dwa całkowicie oddzielne obiekty funkcyjne w dwóch zupełnie różnych momentach [więcej w moim artykule na blogu Double take ]. Jeśli potrzebujesz obsługują IE8 [!!], prawdopodobnie najlepiej jest trzymać się anonimowych wyrażeń funkcji lub deklaracji funkcji , ale unikaj nazwanych wyrażeń funkcji.)
Jedną z kluczowych cech nazwanego wyrażenia funkcji jest to, że tworzy ono identyfikator w zakresie o tej nazwie dla funkcji w treści funkcji:
var x = function example() { console.log(typeof example); // "function" }; x(); console.log(typeof example); // "undefined"
Jednak od ES2015 wiele „anonimowych” wyrażeń funkcyjnych tworzy funkcje z nazwami, a było to poprzedzone przez różne nowoczesne silniki JavaScript, które dość sprytnie kojarzyły nazwy z kontekstem. W ES2015 anonimowe wyrażenie funkcji daje w wyniku funkcję o nazwie
boo
. Jednak nawet z semantyką ES2015 +, automatyczny identyfikator nie jest tworzony:var obj = { x: function() { console.log(typeof x); // "undefined" console.log(obj.x.name); // "x" }, y: function y() { console.log(typeof y); // "function" console.log(obj.y.name); // "y" } }; obj.x(); obj.y();
Przypisanie nazwy funkcji odbywa się za pomocą abstrakcyjnej operacji SetFunctionName używanej w różnych operacjach w specyfikacji.
Krótka wersja jest w zasadzie za każdym razem, gdy anonimowe wyrażenie funkcji pojawia się po prawej stronie czegoś takiego jak przypisanie lub inicjalizacja, na przykład:
var boo = function() { /*...*/ };
(lub może być,
let
aconst
raczej niżvar
) , lubvar obj = { boo: function() { /*...*/ } };
lub
doSomething({ boo: function() { /*...*/ } });
(te dwa ostatnie to tak naprawdę to samo) , wynikowa funkcja będzie miała nazwę (
boo
w przykładach).Istnieje ważny i celowy wyjątek: przypisanie do właściwości istniejącego obiektu:
obj.boo = function() { /*...*/ }; // <== Does not get a name
Wynikało to z obaw związanych z wyciekiem informacji, które pojawiły się podczas dodawania nowej funkcji; szczegóły w mojej odpowiedzi na inne pytanie tutaj .
źródło
new
operatora (nadanie nazw wszystkim takim funkcjom sprawia, że.constructor
właściwość jest bardziej użyteczna podczas debugowania, aby dowiedzieć się, co do cholery jakiś obiekt jest instancją), a dla literałów funkcji przekazywanych bezpośrednio do funkcji bez wcześniejszego przypisania ich właściwości lub zmiennej (npsetTimeout(function () {/*do stuff*/});
.). Nawet Chrome pokazuje je jako,(anonymous function)
chyba że pomożesz im, nazywając je.setTimeout
przykład nie wziął nazwy z zadeklarowanego argumentu formalnegosetTimeout
, gdyby taki miał. :-) Ale tak, NFE są zdecydowanie przydatne, jeśli wiesz, że nie będziesz mieć do czynienia ze starymi przeglądarkami, które robią z nich skrót.Nazywanie funkcji jest przydatne, jeśli muszą odwoływać się do siebie (np. W przypadku wywołań rekurencyjnych). Rzeczywiście, jeśli przekazujesz dosłowne wyrażenie funkcji jako argument bezpośrednio do innej funkcji, to wyrażenie funkcyjne nie może bezpośrednio odwoływać się do siebie w trybie ścisłym ES5, chyba że zostanie nazwane.
Na przykład rozważ ten kod:
setTimeout(function sayMoo() { alert('MOO'); setTimeout(sayMoo, 1000); }, 1000);
Niemożliwe byłoby napisanie tego kodu tak czysto, gdyby przekazane wyrażenie funkcyjne
setTimeout
było anonimowe; zamiast tego musielibyśmy przypisać go do zmiennej przedsetTimeout
wywołaniem. W ten sposób, z nazwanym wyrażeniem funkcyjnym, jest nieco krótszy i schludniejszy.Historycznie było możliwe napisanie takiego kodu nawet przy użyciu anonimowego wyrażenia funkcyjnego, wykorzystując
arguments.callee
...setTimeout(function () { alert('MOO'); setTimeout(arguments.callee, 1000); }, 1000);
... ale
arguments.callee
jest przestarzały i jest całkowicie zabroniony w trybie ścisłym ES5. Dlatego MDN radzi:(podkreślenie moje)
źródło
Jeśli funkcja jest określona jako wyrażenie funkcji, można nadać jej nazwę.
Będzie dostępny tylko wewnątrz funkcji (z wyjątkiem IE8-).
var f = function sayHi(name) { alert( sayHi ); // Inside the function you can see the function code }; alert( sayHi ); // (Error: undefined variable 'sayHi')
Ta nazwa jest przeznaczona dla niezawodnego rekurencyjnego wywołania funkcji, nawet jeśli jest zapisana w innej zmiennej.
Ponadto nazwa NFE (Named Function Expression) MOŻE zostać nadpisana
Object.defineProperty(...)
następującą metodą:var test = function sayHi(name) { Object.defineProperty(test, 'name', { value: 'foo', configurable: true }); alert( test.name ); // foo }; test();
Uwaga: nie można tego zrobić w przypadku deklaracji funkcji. Ta „specjalna” nazwa funkcji wewnętrznej jest określona tylko w składni wyrażenia funkcji.
źródło
Powinieneś zawsze używać nazwanych wyrażeń funkcyjnych, dlatego:
Możesz użyć nazwy tej funkcji, gdy potrzebujesz rekursji.
Funkcje anonimowe nie pomagają podczas debugowania, ponieważ nie widać nazwy funkcji, która powoduje problemy.
Kiedy nie nazwiesz funkcji, później trudniej będzie zrozumieć, co ona robi. Nadanie mu nazwy ułatwia zrozumienie.
var foo = function bar() { //some code... }; foo(); bar(); // Error!
Tutaj, na przykład, ponieważ pasek nazwy jest używany w wyrażeniu funkcyjnym, nie jest deklarowany w zewnętrznym zakresie. W przypadku nazwanych wyrażeń funkcyjnych nazwa wyrażenia funkcyjnego jest ujęta w jego własnym zakresie.
źródło
Używanie nazwanych wyrażeń funkcyjnych jest lepsze, gdy chcesz mieć możliwość odniesienia się do danej funkcji bez konieczności polegania na przestarzałych funkcjach, takich jak
arguments.callee
.źródło