var functionName = function () {} vs function functionName () {}

6871

Niedawno zacząłem utrzymywać kod JavaScript innej osoby. Naprawiam błędy, dodaje funkcje, a także próbuję uporządkować kod i uczynić go bardziej spójnym.

Poprzedni programista zastosował dwa sposoby deklarowania funkcji i nie mogę się zorientować, czy jest to uzasadnione czy nie.

Dwa sposoby to:

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

Jakie są powody korzystania z tych dwóch różnych metod i jakie są zalety i wady każdej z nich? Czy jest coś, co można zrobić za pomocą jednej metody, czego nie można zrobić za pomocą drugiej?

Richard Garside
źródło
198
permadi.com/tutorial/jsFunc/index.html to bardzo dobra strona o funkcjach javascript
uzay95
67
Powiązany jest ten doskonały artykuł na temat nazwanych wyrażeń funkcyjnych .
Phrogz
12
@CMS odwołuje się do tego artykułu: kangax.github.com/nfe/#expr-vs-decl
Upperstage
106
Są dwie rzeczy, o których musisz wiedzieć: # 1 W JavaScript, deklaracje są podnoszone. Oznacza to, że var a = 1; var b = 2;staje się var a; var b; a = 1; b = 2. Kiedy deklarujesz functionOne, zostaje zadeklarowane, ale jego wartość nie jest ustawiana natychmiast. Ponieważ functionTwo to tylko deklaracja, zostaje umieszczona na szczycie zakresu. # 2 functionTwo pozwala uzyskać dostęp do właściwości name, co bardzo pomaga w próbach debugowania.
xavierm02
65
Aha i btw, poprawna składnia to „;” po przydziale i bez po zgłoszeniu. Np . function f(){}Vs. var f = function(){};
xavierm02

Odpowiedzi:

5043

Różnica polega na tym, że functionOnejest wyrażeniem funkcyjnym, a więc jest definiowana tylko po osiągnięciu tego wiersza, podczas gdy functionTwojest deklaracją funkcji i jest definiowana natychmiast po wykonaniu otaczającej funkcji lub skryptu (z powodu podnoszenia ).

Na przykład wyrażenie funkcyjne:

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

I deklaracja funkcji:

// Outputs: "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

Historycznie deklaracje funkcji zdefiniowane w blokach były obsługiwane niespójnie między przeglądarkami. Tryb ścisły (wprowadzony w ES5) rozwiązał ten problem, scopując deklaracje funkcji do ich otaczającego bloku.

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError

Greg
źródło
631
@Greg: Nawiasem mówiąc, różnica polega nie tylko na tym, że są one analizowane w różnych momentach. Zasadniczo twoja functionOnejest jedynie zmienną, do której przypisano anonimową funkcję, podczas gdy w functionTworzeczywistości jest funkcją nazwaną. Zadzwoń .toString()do obu, aby zobaczyć różnicę. Jest to istotne w niektórych przypadkach, gdy chcesz programowo uzyskać nazwę funkcji.
Jason Bunting,
6
@Jason Bunting .. nie jestem pewien, o co tu chodzi, wydaje się, że .toString () zwraca zasadniczo tę samą wartość (definicję funkcji) dla obu: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z
124
Oba są różne. Pierwszy to function expressiondrugi, a drugi to function declaration. Możesz przeczytać więcej na ten temat tutaj: javascriptweblog.wordpress.com/2010/07/06/…
Michał Kuklis
127
@Greg Część odpowiedzi dotycząca czasu analizy w porównaniu do czasu wykonywania jest niepoprawna. W JavaScript deklaracje funkcji nie są definiowane w czasie analizy, ale w czasie wykonywania. Proces wygląda następująco: parsowany jest kod źródłowy -> program JavaScript jest oceniany -> inicjowany jest globalny kontekst wykonania -> wykonywana jest instancja wiązania deklaracji. Podczas tego procesu deklaracje funkcji są tworzone (patrz krok 5 w rozdziale 10.5 ).
Šime Vidas
102
Terminologia tego zjawiska jest znana jako podnoszenie.
Colin Pear,
1941

Najpierw chcę poprawić Grega: function abc(){}ma również abczasięg - nazwa jest zdefiniowana w zakresie, w którym napotyka się tę definicję. Przykład:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

Po drugie, możliwe jest połączenie obu stylów:

var xyz = function abc(){};

xyzzostanie zdefiniowany jak zwykle, nie abcjest zdefiniowany we wszystkich przeglądarkach, ale Internet Explorer - nie polegaj na tym, że jest definiowany. Ale zostanie zdefiniowane w jego ciele:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

Jeśli chcesz aliasować funkcje we wszystkich przeglądarkach, użyj tego rodzaju deklaracji:

function abc(){};
var xyz = abc;

W tym przypadku oba są xyzi abcsą aliasami tego samego obiektu:

console.log(xyz === abc); // prints "true"

Jednym z istotnych powodów, dla których warto zastosować połączony styl, jest atrybut „name” obiektów funkcyjnych ( nie obsługiwany przez Internet Explorer ). Zasadniczo po zdefiniowaniu funkcji takiej jak

function abc(){};
console.log(abc.name); // prints "abc"

jego nazwa jest przypisywana automatycznie. Ale kiedy to zdefiniujesz

var abc = function(){};
console.log(abc.name); // prints ""

jego nazwa jest pusta - stworzyliśmy anonimową funkcję i przypisaliśmy ją do jakiejś zmiennej.

Innym dobrym powodem do użycia połączonego stylu jest użycie krótkiej nazwy wewnętrznej w celu odniesienia się do siebie, przy jednoczesnym zapewnieniu użytkownikom zewnętrznym długiej, nie powodującej konfliktu nazwy:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

W powyższym przykładzie możemy zrobić to samo z zewnętrzną nazwą, ale będzie ona zbyt nieporęczna (i wolniejsza).

(Innym sposobem na odwołanie się do siebie jest użycie arguments.callee, które wciąż jest stosunkowo długie i nie jest obsługiwane w trybie ścisłym).

W głębi duszy JavaScript traktuje obie instrukcje inaczej. To jest deklaracja funkcji:

function abc(){}

abc tutaj jest zdefiniowane wszędzie w bieżącym zakresie:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

Ponadto wyciągnął returnoświadczenie:

// We can call it here
abc(); // Works
return;
function abc(){}

To jest wyrażenie funkcyjne:

var xyz = function(){};

xyz tutaj jest zdefiniowane od miejsca przypisania:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

Deklaracja funkcji a wyrażenie funkcji jest prawdziwym powodem, dla którego istnieje różnica wykazana przez Grega.

Śmieszny fakt:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

Osobiście wolę deklarację „wyrażenie funkcji”, ponieważ w ten sposób mogę kontrolować widoczność. Kiedy zdefiniuję funkcję jak

var abc = function(){};

Wiem, że zdefiniowałem tę funkcję lokalnie. Kiedy zdefiniuję funkcję jak

abc = function(){};

Wiem, że zdefiniowałem to globalnie, pod warunkiem, że nie zdefiniowałem abcnigdzie w łańcuchu zakresów. Ten styl definicji jest elastyczny, nawet gdy jest używany w środku eval(). Podczas definicji

function abc(){};

zależy od kontekstu i może sprawić, że zgadniesz, gdzie jest on faktycznie zdefiniowany, szczególnie w przypadku eval()- odpowiedź brzmi: to zależy od przeglądarki.

Eugene Lazutkin
źródło
69
Mam na myśli RoBorga, ale nigdzie go nie ma. Proste: RoBorg === Greg. W ten sposób można przepisać historię w dobie Internetu. ;-)
Eugene Lazutkin
10
var xyz = funkcja abc () {}; console.log (xyz === abc); Wszystkie przeglądarki, które przetestowałem (Safari 4, Firefox 3.5.5, Opera 10.10) dają mi „Nieokreśloną zmienną: abc”.
NVI,
7
Ogólnie rzecz biorąc, myślę, że ten post dobrze wyjaśnia różnice i zalety korzystania z deklaracji funkcji. Zgadzam się nie zgadzać, jeśli chodzi o korzyści wynikające z użycia przypisań wyrażeń funkcyjnych do zmiennej, zwłaszcza że „korzyść” wydaje się zaleceniem deklarowania globalnej jednostki… i wszyscy wiedzą, że nie powinieneś zaśmiecać globalnej przestrzeni nazw , dobrze? ;-)
natlee75,
83
imo ogromnym powodem do używania nazwanej funkcji jest to, że debuggery mogą używać tej nazwy, aby pomóc ci zrozumieć stos wywołań lub ślad stosu. jest do bani, gdy spojrzysz na stos wywołań i zobaczysz „funkcję anonimową” 10 poziomów głębokości ...
koza
3
var abc = function(){}; console.log(abc.name);nie produkuje ""więcej, ale "abc"zamiast tego.
Qwerty
632

Oto podsumowanie standardowych formularzy tworzących funkcje: (Pierwotnie napisane dla innego pytania, ale dostosowane po przeniesieniu do pytania kanonicznego).

Warunki:

Szybka lista:

  • Deklaracja funkcji

  • functionWyrażenie „anonimowe” (które pomimo tego terminu czasami tworzy funkcje o nazwach)

  • O nazwie function wyrażenie

  • Inicjator funkcji akcesoriów (ES5 +)

  • Wyrażenie funkcji strzałki (ES2015 +) (które, podobnie jak anonimowe wyrażenia funkcji, nie wymaga wyraźnej nazwy, a jednak może tworzyć funkcje o nazwach)

  • Deklaracja metody w inicjalizatorze obiektów (ES2015 +)

  • Deklaracja konstruktora i metody w class(ES2015 +)

Deklaracja funkcji

Pierwsza forma to deklaracja funkcji , która wygląda następująco:

function x() {
    console.log('x');
}

Deklaracja funkcji jest deklaracją ; to nie jest stwierdzenie ani wyrażenie. W związku z tym nie podążasz za nim; (chociaż jest to nieszkodliwe).

Deklaracja funkcji jest przetwarzana, gdy wykonanie wejdzie w kontekst, w którym się pojawia, przed wykonaniem dowolnego kodu krok po kroku. Funkcja, którą tworzy, otrzymuje odpowiednią nazwę (x w powyższym przykładzie), a nazwa ta jest umieszczana w zakresie, w którym pojawia się deklaracja.

Ponieważ jest przetwarzany przed dowolnym kodem krok po kroku w tym samym kontekście, możesz wykonywać następujące czynności:

x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}

Aż ES2015, spec nie obejmowały co silnik JavaScript należy zrobić, jeśli umieścić deklarację funkcji wewnątrz struktury kontrolnej jak try, if, switch, while, itd., Jak poniżej:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

A ponieważ są one przetwarzane przed uruchomieniem kodu krok po kroku, trudno jest wiedzieć, co robić, gdy są w strukturze kontroli.

Chociaż nie zostało to określone do ES2015, było to dopuszczalne rozszerzenie do obsługi deklaracji funkcji w blokach. Niestety (i nieuchronnie) różne silniki robiły różne rzeczy.

Od wersji ES2015 specyfikacja mówi, co robić. W rzeczywistości daje trzy różne rzeczy do zrobienia:

  1. Jeśli w trybie swobodnym nie ma przeglądarki internetowej, silnik JavaScript powinien zrobić jedną rzecz
  2. W trybie luźnym w przeglądarce silnik JavaScript powinien zrobić coś innego
  3. W trybie ścisłym (przeglądarka lub nie) silnik JavaScript powinien zrobić jeszcze jedną rzecz

Reguły dla trybów swobodnych są trudne, ale w trybie ścisłym deklaracje funkcji w blokach są łatwe: są lokalne dla bloku (mają zakres bloku , który jest również nowy w ES2015) i są podnoszone na szczyt bloku. Więc:

"use strict";
if (someCondition) {
    foo();               // Works just fine
    function foo() {
    }
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
                         // because it's not in the same block)

functionWyrażenie „anonimowe”

Druga popularna forma nazywa się anonimowym wyrażeniem funkcji :

var y = function () {
    console.log('y');
};

Podobnie jak wszystkie wyrażenia, jest on oceniany, gdy zostanie osiągnięty podczas wykonywania kodu krok po kroku.

W ES5 utworzona funkcja nie ma nazwy (jest anonimowa). W ES2015 funkcja ma przypisaną nazwę, jeśli to możliwe, na podstawie kontekstu. W powyższym przykładzie nazwą byłoby y. Coś podobnego robi się, gdy funkcja jest wartością inicjatora właściwości. (Aby uzyskać szczegółowe informacje o tym, kiedy to się dzieje i zasady, wyszukajSetFunctionName w specyfikacji  - pojawia się wszędzie ).

Nazwanefunction wyrażenie

Trzecia postać to nazwane wyrażenie funkcyjne („NFE”):

var z = function w() {
    console.log('zw')
};

Ta funkcja ma prawidłową nazwę ( ww tym przypadku). Podobnie jak wszystkie wyrażenia, jest to oceniane, gdy jest osiągane w krokowym wykonaniu kodu. Nazwa funkcji nie jest dodawana do zakresu, w którym pojawia się wyrażenie; nazywa się w zakresie samej funkcji:

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"

Zauważ, że NFE często były źródłem błędów w implementacjach JavaScript. Na przykład IE8 i wcześniejsze obsługują NFE całkowicie niepoprawnie , tworząc dwie różne funkcje w dwóch różnych momentach. Wczesne wersje Safari również miały problemy. Dobrą wiadomością jest to, że obecne wersje przeglądarek (IE9 i nowsze, obecne Safari) nie mają już tych problemów. (Ale od tego pisania, niestety, IE8 jest nadal w powszechnym użyciu, więc używanie NFE z kodem dla Internetu jest ogólnie problematyczne.)

Inicjator funkcji akcesoriów (ES5 +)

Czasami funkcje mogą się wkraść w dużej mierze niezauważone; tak jest w przypadku funkcji akcesoriów . Oto przykład:

var obj = {
    value: 0,
    get f() {
        return this.value;
    },
    set f(v) {
        this.value = v;
    }
};
console.log(obj.f);         // 0
console.log(typeof obj.f);  // "number"

Pamiętaj, że kiedy korzystałem z tej funkcji, nie korzystałem ()! To dlatego, że to funkcja akcesorium dla właściwości. Otrzymujemy i ustawiamy właściwość w normalny sposób, ale za kulisami funkcja jest wywoływana.

Można również tworzyć dostępowe z funkcji Object.defineProperty, Object.definePropertiesoraz mniej znanych drugiego argumentuObject.create .

Wyrażenie funkcji strzałki (ES2015 +)

ES2015 oferuje nam funkcję strzałki . Oto jeden przykład:

var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6

Zobacz, jak ta n => n * 2rzecz ukrywa się wmap() rozmowie? To jest funkcja.

Kilka rzeczy na temat funkcji strzałek:

  1. Nie mają swoich this. Zamiast tego blisko nadthis od kontekstu, w którym są one zdefiniowane. (Zamykają się również argumentsi, w stosownych przypadkach super). Oznacza to, że thisich wnętrze jest takie samo, jak miejsce, w thisktórym zostały utworzone i nie można ich zmienić.

  2. Jak zauważyłeś powyżej, nie używasz słowa kluczowego function; zamiast tego używasz =>.

Powyższy n => n * 2przykład jest jedną z nich. Jeśli masz wiele argumentów do przekazania funkcji, użyj parens:

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6

(Zapamietaj to Array#map przekazuje wpis jako pierwszy argument, a indeks jako drugi).

W obu przypadkach treść funkcji jest tylko wyrażeniem; wartość zwracana przez funkcję będzie automatycznie wynikiem tego wyrażenia (nie używasz wyrażenia jawnego)return ).

Jeśli robisz więcej niż tylko jedno wyrażenie, użyj {}i jawnego return(jeśli musisz zwrócić wartość), jak zwykle:

var a = [
  {first: "Joe", last: "Bloggs"},
  {first: "Albert", last: "Bloggs"},
  {first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
  var rv = a.last.localeCompare(b.last);
  if (rv === 0) {
    rv = a.first.localeCompare(b.first);
  }
  return rv;
});
console.log(JSON.stringify(a));

Wersja bez { ... }jest nazywana funkcją strzałki z treścią wyrażenia lub zwięzłą treścią . (Również: zwięzła funkcja strzałki.) Ta z { ... }definiowaniem ciała jest funkcją strzałki z ciałem funkcji . (Również: pełna funkcja strzałki).

Deklaracja metody w inicjalizatorze obiektów (ES2015 +)

ES2015 pozwala na krótszą formę deklarowania właściwości odwołującej się do funkcji zwanej definicją metody ; To wygląda tak:

var o = {
    foo() {
    }
};

prawie odpowiednikiem w ES5 i wcześniejszych byłoby:

var o = {
    foo: function foo() {
    }
};

różnica (inna niż gadatliwość) polega na tym, że metoda może użyć super, ale funkcja nie. Na przykład, jeśli miałbyś obiekt, który zdefiniował (powiedzmy) valueOfza pomocą składni metody, mógłby go użyć, super.valueOf()aby uzyskać wartość, Object.prototype.valueOfktórą zwróciłoby (zanim prawdopodobnie zrobiłby z nią coś innego), podczas gdy wersja ES5 musiałaby to zrobić Object.prototype.valueOf.call(this).

Oznacza to również, że metoda ma odwołanie do obiektu, na którym została zdefiniowana, więc jeśli ten obiekt jest tymczasowy (na przykład przekazujesz go Object.assignjako jeden z obiektów źródłowych), składnia metody może oznaczać, że obiekt jest zachowany w pamięci, gdy w przeciwnym razie można by go wyrzucić (jeśli silnik JavaScript nie wykryje tej sytuacji i obsłuży ją, jeśli żadna z metod nie zastosuje super).

Deklaracja konstruktora i metody w class(ES2015 +)

ES2015 zapewnia nam classskładnię, w tym deklarowane konstruktory i metody:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

Powyżej znajdują się dwie deklaracje funkcji: jedna dla konstruktora, który otrzymuje nazwę Person, i jedna dla getFullName, która jest funkcją przypisaną Person.prototype.

TJ Crowder
źródło
3
to nazwa wjest po prostu ignorowana?
BiAiB
8
@PellePenna: Nazwy funkcji są przydatne do wielu rzeczy. Moim zdaniem dwie duże postaci to rekurencja, a nazwa funkcji wyświetlana jest w stosach wywołań, śladach wyjątków i tym podobnych.
TJ Crowder
4
@ChaimEliyah - „Akceptacja nie oznacza, że ​​jest to najlepsza odpowiedź, oznacza tylko, że zadziałała dla osoby, która zapytała”. źródło
ScrapCode
6
@AR: Całkiem prawda. Zabawne jest jednak to, że powyżej napisano: „Najlepsze odpowiedzi pojawiają się jako pierwsze, aby zawsze były łatwe do znalezienia”. Ponieważ zaakceptowana odpowiedź pojawia się na pierwszym miejscu nawet w odpowiedziach wyżej głosowanych, trasa może być nieco sprzeczna. ;-) Także trochę niedokładne, jeśli określimy „najlepszy” na podstawie głosów (co nie jest wiarygodne, to po prostu to, co mamy), „najlepsze” odpowiedzi pojawiają się najpierw, jeśli używasz karty „Głosy” - w przeciwnym razie odpowiedzi, które są pierwsze, są aktywne lub najstarsze.
TJ Crowder
1
@TJCrowder: Zgoda. „ustawione według daty” jest czasem denerwujące.
ScrapCode
143

Mówiąc o kontekście globalnym, zarówno varinstrukcja, jak i FunctionDeclarationna końcu utworzą nieusuwalną właściwość na obiekcie globalnym, ale wartość obu można zastąpić .

Subtelna różnica między tymi dwoma sposobami polega na tym, że gdy uruchomiony zostanie proces tworzenia zmiennej (przed faktycznym wykonaniem kodu), wszystkie identyfikatory zadeklarowane za pomocą varzostaną zainicjowane undefined, a identyfikatory używane przez te FunctionDeclarationbędą dostępne od tego momentu, na przykład:

 alert(typeof foo); // 'function', it's already available
 alert(typeof bar); // 'undefined'
 function foo () {}
 var bar = function () {};
 alert(typeof bar); // 'function'

Przypisywanie zdarzeń bar FunctionExpressionodbywa się do czasu wykonania.

Globalna właściwość utworzona przez a FunctionDeclarationmoże być nadpisana bez żadnych problemów, podobnie jak wartość zmiennej, np .:

 function test () {}
 test = null;

Inną oczywistą różnicą między twoimi dwoma przykładami jest to, że pierwsza funkcja nie ma nazwy, ale druga ma ją, co może być bardzo przydatne podczas debugowania (tj. Sprawdzania stosu wywołań).

Jeśli chodzi o edytowany pierwszy przykład ( foo = function() { alert('hello!'); };), jest to niezadeklarowane zadanie, gorąco zachęcam do korzystania ze varsłowa kluczowego.

Przy przypisaniu, bez varinstrukcji, jeśli odnośny identyfikator nie zostanie znaleziony w łańcuchu zasięgu, stanie się usuwalną właściwością obiektu globalnego.

Ponadto niezadeklarowane zadania wrzucają ReferenceErrorECMAScript 5 w trybie ścisłym .

Musisz to przeczytać:

Uwaga : Ta odpowiedź została połączona z innym pytaniem , w którym główną wątpliwością i błędnym przekonaniem PO było to, że identyfikatory zadeklarowane za pomocą FunctionDeclaration, nie mogą zostać zastąpione, co nie jest prawdą.

CMS
źródło
Nie wiedziałem, że funkcje mogą zostać nadpisane w JavaScript! Poza tym to zamówienie parsowania jest dla mnie wielką zaletą. Chyba muszę obserwować, jak tworzę funkcje.
Xeoncross,
2
+0 do artykułu „Wyrażenia funkcji nazw odszyfrowane”, ponieważ jest to 404ing. Możliwe lustro ?: kangax.github.com/nfe
Mr_Chimp
@CMS Nice one. Pamiętaj jednak, że nigdy nie widziałem oryginału, więc nie wiem, czy to lustro, czy tylko kolejny artykuł o tym samym tytule!
Mr_Chimp
@Mr_Chimp Jestem pewien, że tak, thewaybackmachine mówi, że dostał 302 w czasie indeksowania, a przekierowanie było na podany przez ciebie link.
John
123

Dwa fragmenty kodu, które tam opublikowałeś, będą działały tak samo, dla prawie wszystkich celów.

Różnica w zachowaniu polega jednak na tym, że w pierwszym wariancie ( var functionOne = function() {}) tę funkcję można wywołać dopiero po tym punkcie kodu.

W drugim wariancie ( function functionTwo()) funkcja jest dostępna dla kodu działającego powyżej miejsca, w którym funkcja jest zadeklarowana.

Jest tak, ponieważ w pierwszym wariancie funkcja jest przypisana do zmiennej foow czasie wykonywania. W drugim przypadku funkcja jest przypisana do tego identyfikatora foow czasie analizy.

Więcej informacji technicznych

JavaScript ma trzy sposoby definiowania funkcji.

  1. Twój pierwszy fragment zawiera wyrażenie funkcyjne . Wymaga to użycia operatora „funkcji” do utworzenia funkcji - wynik tego operatora można zapisać w dowolnej zmiennej lub właściwości obiektu. Wyrażenie funkcji jest w ten sposób potężne. Wyrażenie funkcji jest często nazywane „funkcją anonimową”, ponieważ nie musi mieć nazwy,
  2. Twój drugi przykład to deklaracja funkcji . Używa instrukcji „function” do utworzenia funkcji. Funkcja jest dostępna w czasie analizy i można ją wywołać w dowolnym miejscu w tym zakresie. Nadal możesz przechowywać go we właściwości zmiennej lub obiektu później.
  3. Trzecim sposobem definiowania funkcji jest konstruktor „Function ()” , który nie jest pokazany w oryginalnym poście. Nie zaleca się korzystania z tego, ponieważ działa on w taki sam sposób, jak w eval()przypadku problemów.
thomasrutter
źródło
103

Lepsze wyjaśnienie odpowiedzi Grega

functionTwo();
function functionTwo() {
}

Dlaczego nie ma błędu? Zawsze uczono nas, że wyrażenia są wykonywane od góry do dołu (??)

Ponieważ:

Deklaracje funkcji i deklaracje zmiennych są zawsze hoistedniewidocznie przenoszone ( ) niewidocznie na wierzch ich zakresu zawierającego przez interpreter JavaScript. Parametry funkcji i nazwy zdefiniowane w języku są oczywiście już dostępne. Ben Cherry

Oznacza to, że taki kod:

functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };

Zauważ, że część przypisania deklaracji nie została podniesiona. Podnoszona jest tylko nazwa.

Ale w przypadku deklaracji funkcji podniesiony zostanie również cały obiekt funkcji :

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------
suhailvs
źródło
Witam, dziękuję za jasne informacje na temat funkcji. Teraz mam pytanie, która będzie pierwszą deklaracją w hierarchii deklaracji, czy deklaracja zmiennej (functionOne) czy deklaracja funkcji (functionTwo)?
Sharathi RB
91

Inni komentatorzy opisali już różnicę semantyczną dwóch powyższych wariantów. Chciałem zauważyć różnicę stylistyczną: tylko odmiana „przypisania” może ustawić właściwość innego obiektu.

Często buduję moduły JavaScript z takim wzorcem:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

Dzięki temu wzorcowi wszystkie funkcje publiczne będą używać przypisania, a funkcje prywatne - deklaracji.

(Należy również pamiętać, że przypisanie powinno wymagać średnika po instrukcji, podczas gdy deklaracja tego zabrania).

Sean McMillan
źródło
4
O ile wiem, yuiblog.com/blog/2007/06/12/module-pattern jest pierwotnym odniesieniem do wzorca modułu. (Chociaż w tym artykule użyto var foo = function(){...}składni nawet w przypadku zmiennych prywatnych.
Sean McMillan
W rzeczywistości nie jest to do końca prawdą w niektórych starszych wersjach IE. ( function window.onload() {}było coś.)
Ry-
77

Ilustracją tego, kiedy preferować pierwszą metodę od drugiej, jest to, kiedy należy unikać zastępowania poprzednich definicji funkcji.

Z

if (condition){
    function myfunction(){
        // Some code
    }
}

, ta definicja myfunctionzastąpi każdą poprzednią definicję, ponieważ zostanie wykonana w czasie analizy.

Podczas

if (condition){
    var myfunction = function (){
        // Some code
    }
}

wykonuje prawidłowe zadanie definiowania myfunctiontylko wtedy, gdy conditionjest spełniony.

Mbengue Assane
źródło
1
ten przykład jest dobry i jest bliski doskonałości, ale można go ulepszyć. lepszym przykładem byłoby zdefiniowanie var myFunc = null;poza pętlą lub poza blokiem if / elseif / else. Następnie możesz warunkowo przypisać różne funkcje do tej samej zmiennej. W JS lepszym rozwiązaniem jest przypisanie brakującej wartości wartości null, a następnie niezdefiniowanej. Dlatego najpierw powinieneś zadeklarować myFunction jako null, a następnie przypisać go później, warunkowo.
Alexander Mills
62

Ważnym powodem jest dodanie jednej i tylko jednej zmiennej jako „root” przestrzeni nazw ...

var MyNamespace = {}
MyNamespace.foo= function() {

}

lub

var MyNamespace = {
  foo: function() {
  },
  ...
}

Istnieje wiele technik dotyczących przestrzeni nazw. Staje się to coraz ważniejsze dzięki mnogości dostępnych modułów JavaScript.

Zobacz także Jak zadeklarować przestrzeń nazw w JavaScript?

Obrabować
źródło
3
Wygląda na to, że odpowiedź została połączona z tym pytaniem z innego pytania, a sformułowanie może wydawać się trochę niezwiązane z tym pytaniem. Czy zastanowiłbyś się nad edytowaniem odpowiedzi, aby była bardziej ukierunkowana na to pytanie? (powtarzam; to wcale nie jest twoja wina ... tylko efekt uboczny połączonego pytania). Możesz go również usunąć i myślę, że zachowałbyś swoją reputację. Lub możesz to zostawić; ponieważ jest stary, może nie mieć większego znaczenia.
Andrew Barber
55

Podnoszenie to działanie interpretera JavaScript polegające na przeniesieniu wszystkich deklaracji zmiennych i funkcji na szczyt bieżącego zakresu.

Jednak podnoszone są tylko rzeczywiste deklaracje. pozostawiając zadania tam, gdzie są.

  • Zadeklarowane na stronie zmienne / funkcje są globalne i mają dostęp do dowolnego miejsca na tej stronie.
  • zmienne / Funkcje zadeklarowane wewnątrz funkcji mają zasięg lokalny. oznacza, że ​​są dostępne / dostępne w treści funkcji (zakres), nie są dostępne poza treścią funkcji.

Zmienna

JavaScript jest nazywany językiem luźno pisanym. Co oznacza, że ​​zmienne JavaScript mogą przechowywać wartość dowolnego typu danych . Javascript automatycznie dba o zmianę typu zmiennej na podstawie wartości / literału podanych podczas działania.

global_Page = 10;                                               var global_Page;      « undefined
    « Integer literal, Number Type.   -------------------       global_Page = 10;     « Number         
global_Page = 'Yash';                 |   Interpreted   |       global_Page = 'Yash'; « String
    « String literal, String Type.    «       AS        «       global_Page = true;   « Boolean 
var global_Page = true;               |                 |       global_Page = function (){          « function
    « Boolean Type                    -------------------                 var local_functionblock;  « undefined
global_Page = function (){                                                local_functionblock = 777 Number
    var local_functionblock = 777;                              };  
    // Assigning function as a data.
};  

Funkcjonować

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • funkcje zadeklarowane na stronie są podnoszone na górę strony z dostępem globalnym.
  • funkcje zadeklarowane w bloku funkcyjnym są podnoszone do góry bloku.
  • Domyślna wartość zwracana przez funkcję to „ niezdefiniowana ”, domyślna wartość deklaracji zmiennej również „niezdefiniowana”

    Scope with respect to function-block global. 
    Scope with respect to page undefined | not available.

Deklaracja funkcji

function globalAccess() {                                  function globalAccess() {      
}                                  -------------------     }
globalAccess();                    |                 |     function globalAccess() { « Re-Defined / overridden.
localAccess();                     «   Hoisted  As   «         function localAccess() {
function globalAccess() {          |                 |         }
     localAccess();                -------------------         localAccess(); « function accessed with in globalAccess() only.
     function localAccess() {                              }
     }                                                     globalAccess();
}                                                          localAccess(); « ReferenceError as the function is not defined

Wyrażenie funkcji

        10;                 « literal
       (10);                « Expression                (10).toString() -> '10'
var a;                      
    a = 10;                 « Expression var              a.toString()  -> '10'
(function invoke() {        « Expression Function
 console.log('Self Invoking');                      (function () {
});                                                               }) () -> 'Self Invoking'

var f; 
    f = function (){        « Expression var Function
    console.log('var Function');                                   f ()  -> 'var Function'
    };

Funkcja przypisana do zmiennej Przykład:

(function selfExecuting(){
    console.log('IIFE - Immediately-Invoked Function Expression');
}());

var anonymous = function (){
    console.log('anonymous function Expression');
};

var namedExpression = function for_InternalUSE(fact){
    if(fact === 1){
        return 1;
    }

    var localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    //return; //undefined.
    return fact * for_InternalUSE( fact - 1);   
};

namedExpression();
globalExpression();

javascript interpretowany jako

var anonymous;
var namedExpression;
var globalExpression;

anonymous = function (){
    console.log('anonymous function Expression');
};

namedExpression = function for_InternalUSE(fact){
    var localExpression;

    if(fact === 1){
        return 1;
    }
    localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    return fact * for_InternalUSE( fact - 1);    // DEFAULT UNDEFINED.
};

namedExpression(10);
globalExpression();

Możesz sprawdzić deklarację funkcji, test ekspresji przy użyciu różnych przeglądarek jsperf Test Runner


Klasy funkcji konstruktora ES5 : Obiekty funkcji utworzone za pomocą Function.prototype.bind

JavaScript traktuje funkcje jak obiekty pierwszej klasy, więc będąc obiektem, możesz przypisywać właściwości do funkcji.

function Shape(id) { // Function Declaration
    this.id = id;
};
    // Adding a prototyped method to a function.
    Shape.prototype.getID = function () {
        return this.id;
    };
    Shape.prototype.setID = function ( id ) {
        this.id = id;
    };

var expFn = Shape; // Function Expression

var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10

ES6 wprowadził funkcję strzałki: Wyrażenie funkcji strzałki ma krótszą składnię, najlepiej nadają się do funkcji niemetodowych i nie można ich używać jako konstruktorów.

ArrowFunction : ArrowParameters => ConciseBody.

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd
Yash
źródło
3
ahm, twoja odpowiedź ... czy to nie jest dwuznaczne? dobrze napisane, więc +1 za wydatki i pisanie zbyt dużej ilości informacji.
duński
40

Dodam własną odpowiedź tylko dlatego, że wszyscy dokładnie zakryli część podnoszącą.

Od dłuższego czasu zastanawiałem się, która droga jest lepsza, a dzięki http://jsperf.com teraz wiem :)

wprowadź opis zdjęcia tutaj

Deklaracje funkcji są szybsze, a to naprawdę ma znaczenie w web dev, prawda? ;)

Leon Gaban
źródło
8
Powiedziałbym, że łatwość konserwacji jest najważniejszym aspektem większości kodu. Wydajność jest ważna, ale w większości przypadków IO może być większym wąskim gardłem w sposobie definiowania funkcji. Istnieją jednak pewne problemy, w których potrzebujesz każdej możliwej wydajności, co jest przydatne w takich przypadkach. Dobrze też mieć tutaj odpowiedź, która wyraźnie odpowiada dobrze zdefiniowaną część pytania.
Richard Garside
3
Cóż, odkryłem, że jest inaczej z Firefoxem. jsperf.com/sandytest
Sandeep Nayak
Tylko aktualizacja, odkąd przeszedłem w pełni funkcjonalny styl programowania w JavaScript, nigdy nie używam deklaracji, tylko wyrażenia funkcyjne, dzięki czemu mogę łączyć i wywoływać moje funkcje według ich nazw zmiennych. Sprawdź RamdaJS ...
Leon Gaban
1
@ SandeepNayak Właśnie uruchomiłem własny test w Firefoksie 50.0.0 / Windows 7 0.0.0 i tak naprawdę jest tak samo jak Leon. Więc jeśli twój test jest poprawny, wyciągnę wniosek, że testy jsperf nie są wskaźnikowe i wszystko zależy od twojej przeglądarki i / lub wersji systemu operacyjnego lub od konkretnego stanu bieżącej maszyny w danym momencie.
ocramot
33

Deklaracja funkcji i wyrażenie funkcji przypisane do zmiennej zachowują się tak samo po ustanowieniu powiązania.

Istnieje jednak różnica w tym, jak i kiedy obiekt funkcji jest faktycznie powiązany z jego zmienną. Różnica wynika z mechanizmu zwanego podnoszeniem zmiennych w JavaScript.

Zasadniczo wszystkie deklaracje funkcji i deklaracje zmiennych są przenoszone na szczyt funkcji, w której występuje deklaracja (dlatego mówimy, że JavaScript ma zakres funkcji ).

  • Kiedy deklaracja funkcji jest podnoszona, ciało funkcji „podąża”, więc gdy ciało funkcji jest oceniane, zmienna zostanie natychmiast powiązana z obiektem funkcji.

  • Gdy podnoszona jest deklaracja zmiennej, inicjalizacja nie następuje, ale zostaje „pozostawiona”. Zmienna jest inicjalizowana undefinedna początku treści funkcji i zostanie przypisana wartość w pierwotnym miejscu w kodzie. (W rzeczywistości zostanie mu przypisana wartość w każdym miejscu, w którym występuje deklaracja zmiennej o tej samej nazwie).

Ważna jest również kolejność podnoszenia: deklaracje funkcji mają pierwszeństwo przed deklaracjami zmiennych o tej samej nazwie, a ostatnia deklaracja funkcji ma pierwszeństwo przed poprzednimi deklaracjami funkcji o tej samej nazwie.

Kilka przykładów...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

Zmienna foojest podnoszony do góry funkcji, zainicjowanym undefined, tak że !foojest true, to foojest przypisany 10. fooPoza bar„s zakresie odgrywa żadnej roli i jest nietknięte.

function f() {
  return a; 
  function a() {return 1}; 
  var a = 4;
  function a() {return 2}}
f()() // 2

function f() {
  return a;
  var a = 4;
  function a() {return 1};
  function a() {return 2}}
f()() // 2

Deklaracje funkcji mają pierwszeństwo przed deklaracjami zmiennych, a ostatnia deklaracja funkcji „trzyma się”.

function f() {
  var a = 4;
  function a() {return 1}; 
  function a() {return 2}; 
  return a; }
f() // 4

W tym przykładzie ajest inicjowany obiektem funkcji wynikającym z oceny drugiej deklaracji funkcji, a następnie jest przypisywany 4.

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}}
b();
a // 1

Tutaj deklaracja funkcji jest najpierw podnoszona, deklarując i inicjując zmienną a. Następnie ta zmienna jest przypisywana 10. Innymi słowy: przypisanie nie przypisuje zmiennej zewnętrznej a.

eljenso
źródło
3
Masz dziwny sposób na umieszczenie zamków zamykających. Czy jesteś programistą Python? Wygląda na to, że starasz się, aby JavaScript wyglądał jak Python. Obawiam się, że jest to mylące dla innych ludzi. Gdybym musiał zachować kod JavaScript, najpierw przepuściłbym twój kod przez automatyczny program prettyprinter.
dokładnie
1
Doskonały post. „Wykonywanie funkcji” lub „natychmiastowe wywołanie wyrażenia funkcji” powinno być wystarczająco łatwe do zauważenia, a jego preferencje stylu nie powinny odwracać uwagi od jego stanowiska - co jest dokładne i doskonale podsumowuje „podnoszenie”. +1
Ricalsin
32

Pierwszym przykładem jest deklaracja funkcji:

function abc(){}

Drugi przykład to wyrażenie funkcyjne:

var abc = function() {};

Główną różnicą jest sposób ich podnoszenia (podnoszenia i deklarowania). W pierwszym przykładzie podnoszona jest cała deklaracja funkcji. W drugim przykładzie podnoszony jest tylko var 'abc', jego wartość (funkcja) będzie niezdefiniowana, a sama funkcja pozostanie w pozycji, w której została zadeklarowana.

Krótko mówiąc:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}

Aby dowiedzieć się więcej na ten temat, zdecydowanie polecam ten link

sla55er
źródło
1
Twój przykład jest trochę taki sam jak najlepsza odpowiedź
GôTô
Głównym powodem opublikowania tej odpowiedzi było podanie linku na dole. Tego brakowało mi do pełnego zrozumienia powyższego pytania.
sla55er
1
To bardzo fajne, że chcesz udostępnić link. Jednak linki do dodatkowych informacji w SO powinny być komentarzem do pytania lub ulubionej odpowiedzi. Bardzo nieoptymalne jest zaśmiecanie długiej, skomplikowanej strony takiej jak ta z powtarzającymi się informacjami, aby dodać pojedynczy przydatny link na jej końcu. Nie, nie otrzymasz punktów przedstawicielskich za udostępnienie linku, ale będziesz pomagać społeczności.
XML
30

Pod względem kosztów utrzymania kodu, nazwane funkcje są bardziej preferowane:

  • Niezależne od miejsca, w którym zostały zadeklarowane (ale nadal ograniczone zakresem).
  • Bardziej odporny na błędy, takie jak inicjalizacja warunkowa (nadal możesz zastąpić, jeśli chcesz).
  • Kod staje się bardziej czytelny dzięki alokacji funkcji lokalnych oddzielnie od funkcji zakresu. Zwykle w zakresie funkcjonalność jest pierwsza, a następnie deklaracje funkcji lokalnych.
  • W debuggerze wyraźnie zobaczysz nazwę funkcji na stosie wywołań zamiast funkcji „anonimowej / ocenionej”.

Podejrzewam, że następują kolejne PROS dla nazwanych funkcji. Zaletą nazwanych funkcji jest wada anonimowych.

Historycznie, anonimowe funkcje powstały z niemożności JavaScript jako języka do listy członków o nazwanych funkcjach:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
Sasha Firsov
źródło
2
Istnieje test do potwierdzenia: blog.firsov.net/2010/01/... JS test wydajności - zakres i nazwane funkcje - Analytics
Sasha Firsov
25

W kategoriach informatyki mówimy o funkcjach anonimowych i nazwanych. Myślę, że najważniejszą różnicą jest to, że funkcja anonimowa nie jest związana z nazwą, stąd nazwa funkcji anonimowej. W JavaScript jest to obiekt pierwszej klasy dynamicznie zadeklarowany w czasie wykonywania.

Aby uzyskać więcej informacji na temat funkcji anonimowych i rachunku lambda, Wikipedia jest dobrym początkiem ( http://en.wikipedia.org/wiki/Anonymous_function ).

Kafka
źródło
Począwszy od ES2015 (sześć i pół roku po opublikowaniu odpowiedzi) obie funkcje w pytaniu są nazwane.
TJ Crowder
25

Używam podejścia zmiennego w moim kodzie z bardzo konkretnego powodu, którego teoria została omówiona powyżej w sposób abstrakcyjny, ale przykład może pomóc niektórym osobom takim jak ja, z ograniczoną znajomością JavaScript.

Mam kod, który muszę uruchomić z 160 niezależnie zaprojektowanymi brandami. Większość kodu znajduje się we współdzielonych plikach, ale elementy specyficzne dla marki znajdują się w osobnym pliku, po jednym dla każdej marki.

Niektóre marki wymagają określonych funkcji, a niektóre nie. Czasami muszę dodawać nowe funkcje, aby wykonywać nowe czynności związane z brandingiem. Cieszę się, że mogę zmienić wspólne kodowanie, ale nie chcę zmieniać wszystkich 160 zestawów plików brandingowych.

Korzystając ze składni zmiennej, mogę zadeklarować zmienną (zasadniczo wskaźnik funkcji) we wspólnym kodzie i albo przypisać trywialną funkcję pośredniczącą, albo ustawić wartość null.

Jeden lub dwa brandingi, które wymagają konkretnej implementacji funkcji, mogą następnie zdefiniować swoją wersję funkcji i przypisać ją do zmiennej, jeśli chcą, a reszta nic nie robi. Mogę przetestować funkcję zerową przed wykonaniem jej we wspólnym kodzie.

Z powyższych komentarzy ludzi wynika, że ​​może być również możliwe przedefiniowanie funkcji statycznej, ale myślę, że zmienne rozwiązanie jest ładne i jasne.

Herc
źródło
25

Odpowiedź Grega jest wystarczająco dobra, ale nadal chciałbym dodać do niej coś, czego nauczyłem się właśnie oglądając filmy Douglasa Crockforda .

Wyrażenie funkcji:

var foo = function foo() {};

Instrukcja funkcji:

function foo() {};

Instrukcja funkcji jest tylko skrótem dla varinstrukcji o functionwartości.

Więc

function foo() {};

rozwija się do

var foo = function foo() {};

Który rozszerza się dalej do:

var foo = undefined;
foo = function foo() {};

I oba są podnoszone na szczyt kodu.

Zrzut ekranu z wideo

Rohan
źródło
7
Przepraszam, ale to nieprawda - nie wiem, co Crockford próbuje powiedzieć w tym slajdzie. Zarówno deklaracje funkcyjne, jak i zmienne są zawsze podnoszone na szczyt zakresu. Różnica polega na tym, że przypisania zmiennych (niezależnie od tego, czy przypisujesz je łańcuchem, wartością logiczną, czy funkcją), nie są podnoszone na górę, podczas gdy ciała funkcji (przy użyciu deklaracji funkcji) są.
Thomas Heymann
Spójrz na te przykłady kodu: gist.github.com/cyberthom/36603fbc20de8e04fd09
Thomas Heymann
23

Istnieją cztery godne uwagi porównania między dwiema różnymi deklaracjami funkcji wymienionymi poniżej.

  1. Dostępność (zakres) funkcji

Następujące działa, ponieważ function add()jest ograniczone do najbliższego bloku:

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

function add(a, b){
  return a + b;
}

Poniższe nie działa, ponieważ zmienna jest wywoływana przed przypisaniem wartości funkcji do zmiennej add.

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function(a, b){
  return a + b;
}

Powyższy kod ma identyczną funkcjonalność jak poniższy kod. Zauważ, że jawne przypisywanie add = undefinedjest zbędne, ponieważ samo robienie var add;jest dokładnie takie samo jak var add=undefined.

var add = undefined;

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

add = function(a, b){
  return a + b;
}

Poniższe nie działa, ponieważ var add=zastępuje function add().

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function add(a, b){
  return a + b;
}

  1. (funkcja) .nazwa

Nazwa funkcji function thefuncname(){}to nazwafunkcji, gdy jest deklarowana w ten sposób.

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

W przeciwnym razie, jeśli funkcja zostanie zadeklarowana jako function(){}, funkcja .nazwa jest pierwszą zmienną używaną do przechowywania funkcji.

var a = function(){};
var b = (function(){ return function(){} });

console.log(a.name);
console.log(b.name);

Jeśli nie ma żadnych zmiennych ustawionych dla funkcji, nazwa funkcji jest pustym ciągiem ( "").

console.log((function(){}).name === "");

Wreszcie, podczas gdy zmienna, do której funkcja jest przypisana, początkowo ustawia nazwę, kolejne zmienne ustawione dla funkcji nie zmieniają nazwy.

var a = function(){};
var b = a;
var c = b;

console.log(a.name);
console.log(b.name);
console.log(c.name);

  1. Wydajność

W Google V8 i Spidermonkey w Firefoksie może występować kilka mikrosekundowych różnic w kompilacji JIST, ale ostatecznie wynik jest dokładnie taki sam. Aby to udowodnić, przyjrzyjmy się wydajności JSPerf przy znakach mikrodruku, porównując szybkość dwóch pustych fragmentów kodu. Te testy JSPerf są tutaj znaleźć . I testy jsben.ch znajdują się tutaj . Jak widać, zauważalna jest różnica, kiedy nie powinno jej być. Jeśli naprawdę jesteś maniakiem wydajności, takim jak ja, może być bardziej warte wysiłku, starając się zmniejszyć liczbę zmiennych i funkcji w zakresie, a zwłaszcza eliminując polimorfizm (np. Używając tej samej zmiennej do przechowywania dwóch różnych typów).

  1. Zmienna zmienność

Kiedy używasz varsłowa kluczowego do deklarowania zmiennej, możesz następnie przypisać inną wartość do zmiennej w ten sposób.

(function(){
    "use strict";
    var foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Kiedy jednak użyjemy instrukcji const, odwołanie do zmiennej staje się niezmienne. Oznacza to, że nie możemy przypisać nowej wartości do zmiennej. Należy jednak pamiętać, że nie powoduje to, że zawartość zmiennej jest niezmienna: jeśli tak const arr = [], to nadal można arr[10] = "example". Tylko zrobienie czegoś podobnego arr = "new value"lub arr = []spowodowałoby błąd, jak pokazano poniżej.

(function(){
    "use strict";
    const foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Co ciekawe, jeśli zadeklarujemy zmienną jako function funcName(){}, wówczas niezmienność zmiennej jest taka sama, jak jej zadeklarowanie var.

(function(){
    "use strict";
    function foobar(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Co to jest „najbliższy blok”

„Najbliższy blok” to najbliższa „funkcja” (w tym funkcje asynchroniczne, funkcje generatora i funkcje asynchronicznego generatora). Jednak, co ciekawe, function functionName() {}zachowuje się jak w var functionName = function() {}przypadku bloku, który nie jest zamknięty dla przedmiotów poza wspomnianym zamknięciem. Przestrzegać.

  • Normalna var add=function(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}');
  }
} catch(e) {
  console.log("Is a block");
}
var add=function(a, b){return a + b}

  • Normalna function add(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
function add(a, b){
  return a + b;
}

  • Funkcjonować

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(function () {
    function add(a, b){
      return a + b;
    }
})();

  • Komunikat (na przykład if, else, for, while, try/ catch/ finally, switch, do/ while, with)

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
{
    function add(a, b){
      return a + b;
    }
}

  • Funkcja strzałki z var add=function()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    var add=function(a, b){
      return a + b;
    }
})();

  • Funkcja strzałki z function add()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    function add(a, b){
      return a + b;
    }
})();

Jack Giffin
źródło
To zasługuje na zaakceptowaną i najbardziej pozytywną odpowiedź
Aaron John Sabu
18

@EugeneLazutkin podaje przykład, w którym nazywa przypisaną funkcję, aby móc jej używaćshortcut() jako wewnętrznego odwołania do siebie. John Resig podaje inny przykład - kopiowanie funkcji rekurencyjnej przypisanej do innego obiektu w swoim samouczku Learning Advanced JavaScript . Chociaż przypisywanie funkcji do właściwości nie jest tu dokładnie pytaniem, polecam aktywnie wypróbować samouczek - uruchom kod, klikając przycisk w prawym górnym rogu, i dwukrotnie kliknij kod, aby edytować według własnych upodobań.

Przykłady z samouczka: wywołania rekurencyjne w yell():

Testy kończą się niepowodzeniem po usunięciu oryginalnego obiektu ninja. (strona 13)

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
}

Jeśli nazwiesz funkcję, która będzie wywoływana rekurencyjnie, testy zakończą się pomyślnie. (strona 14)

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
Joel Purra
źródło
17

Inną różnicą, która nie została wymieniona w innych odpowiedziach, jest to, że jeśli używasz funkcji anonimowej

var functionOne = function() {
    // Some code
};

i użyj tego jako konstruktora jak w

var one = new functionOne();

wtedy one.constructor.namenie zostanie zdefiniowany. Function.namejest niestandardowy, ale jest obsługiwany przez Firefox, Chrome, inne przeglądarki pochodzące z Webkit i IE 9+.

Z

function functionTwo() {
    // Some code
}
two = new functionTwo();

możliwe jest pobranie nazwy konstruktora jako ciągu za pomocą two.constructor.name.

Ingo Kegel
źródło
Nazwa w pierwszym przypadku nie zostanie zdefiniowana, ponieważ jej anonimowa funkcja jest przypisana do zmiennej. Myślę, że słowo zostało wymyślone anonimowy na rzeczy, które nie mają ich nazwy zdefiniowane :)
Om Shankar
W tym przykładzie dwa = nowe staje się funkcją globalną, ponieważ brak var
Waqas Tahir
16

Pierwszy (funkcja doSomething (x)) powinien być częścią notacji obiektowej.

Drugi ( var doSomething = function(x){ alert(x);}) polega po prostu na utworzeniu anonimowej funkcji i przypisaniu jej do zmiennej,doSomething . Zatem doSomething () wywoła tę funkcję.

Możesz chcieć wiedzieć, czym jest deklaracja funkcji i wyrażenie funkcji .

Deklaracja funkcji definiuje nazwaną zmienną funkcyjną bez konieczności przypisywania zmiennej. Deklaracje funkcji występują jako samodzielne konstrukcje i nie można ich zagnieżdżać w blokach niefunkcjonalnych.

function foo() {
    return 3;
}

ECMA 5 (13.0) definiuje składnię jako
identyfikator funkcji (FormalParameterList opt ) {FunctionBody}

W powyższym stanie nazwa funkcji jest widoczna w jej zakresie i zakresie jej rodzica (w przeciwnym razie byłby nieosiągalny).

I w wyrażeniu funkcyjnym

Wyrażenie funkcyjne definiuje funkcję jako część większej składni wyrażenia (zazwyczaj przypisanie zmiennej). Funkcje zdefiniowane za pomocą wyrażeń funkcji mogą być nazwane lub anonimowe. Wyrażenia funkcyjne nie powinny zaczynać się od „funkcja”.

// Anonymous function expression
var a = function() {
    return 3;
}

// Named function expression
var a = function foo() {
    return 3;
}

// Self-invoking function expression
(function foo() {
    alert("hello!");
})();

ECMA 5 (13.0) definiuje składnię jako
funkcję Identyfikator opt (FormalParameterList opt ) {FunctionBody}

NullPoiиteя
źródło
16

Poniżej wymieniam różnice:

  1. Deklarację funkcji można umieścić w dowolnym miejscu kodu. Nawet jeśli zostanie wywołana, zanim definicja pojawi się w kodzie, zostanie wykonana, gdy deklaracja funkcji zostanie zapisana w pamięci lub w taki sposób, że zostanie podniesiona, zanim jakikolwiek inny kod na stronie rozpocznie wykonywanie.

    Spójrz na poniższą funkcję:

    function outerFunction() {
        function foo() {
           return 1;
        }
        return foo();
        function foo() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 2

    Wynika to z faktu, że podczas wykonywania wygląda to tak:

    function foo() {  // The first function declaration is moved to top
        return 1;
    }
    function foo() {  // The second function declaration is moved to top
        return 2;
    }
    function outerFunction() {
        return foo();
    }
    alert(outerFunction()); //So executing from top to bottom,
                            //the last foo() returns 2 which gets displayed

    Wyrażenie funkcyjne, jeśli nie zostanie zdefiniowane przed wywołaniem, spowoduje błąd. Również tutaj sama definicja funkcji nie jest przenoszona na górę ani zapisywana w pamięci, jak w deklaracjach funkcji. Ale zmienna, do której przypisujemy funkcję, zostaje podniesiona i niezdefiniowana zostaje do niej przypisana.

    Ta sama funkcja przy użyciu wyrażeń funkcyjnych:

    function outerFunction() {
        var foo = function() {
           return 1;
        }
        return foo();
        var foo = function() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 1

    Wynika to z faktu, że podczas wykonywania wygląda to tak:

    function outerFunction() {
       var foo = undefined;
       var foo = undefined;
    
       foo = function() {
          return 1;
       };
       return foo ();
       foo = function() {   // This function expression is not reachable
          return 2;
       };
    }
    alert(outerFunction()); // Displays 1
  2. Zapisywanie deklaracji funkcji w blokach niefunkcyjnych nie jest bezpieczne, na przykład jeśli nie będą one dostępne.

    if (test) {
        function x() { doSomething(); }
    }
  3. Nazwane wyrażenie funkcyjne, takie jak poniższe, może nie działać w przeglądarkach Internet Explorer wcześniejszych niż wersja 9.

    var today = function today() {return new Date()}
Warna
źródło
1
@Arjun Jaki jest problem, jeśli pytanie zostało zadane lata wcześniej? Odpowiedź jest korzystna nie tylko dla PO, ale potencjalnie dla wszystkich użytkowników SO, bez względu na to, kiedy pytanie zostało zadane. A co jest złego w odpowiadaniu na pytania, które już mają zaakceptowaną odpowiedź?
SantiBailors
1
@Arjun musisz zrozumieć, że odpowiadanie na stare pytania nie jest złe. Gdyby tak było, SO miałaby taką barierę. Wyobraź sobie, że nastąpiła zmiana w interfejsie API (choć nie w kontekście tego pytania) i ktoś ją dostrzega i udziela odpowiedzi w nowym interfejsie API, czy nie powinno to być dozwolone? Dopóki odpowiedź nie ma sensu i nie pasuje tutaj, zostanie automatycznie oceniona i usunięta. Nie musisz się tym przejmować !!!!
Sudhansu Choudhary,
15

Jeśli użyjesz tych funkcji do tworzenia obiektów, otrzymasz:

var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function

var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function
Paweł Furmaniak
źródło
Nie mogę tego odtworzyć. console.log(objectOne.__proto__);wypisuje „functionOne {}” na mojej konsoli. Wszelkie pomysły, dlaczego tak może być?
Mike
Nie mogę też tego odtworzyć.
daremkd
1
Jest to zdolność twojego debuggera (do wyświetlania „klasy” rejestrowanego obiektu), a większość z nich jest w stanie uzyskać nazwę nawet dla anonimowych wyrażeń funkcji w dzisiejszych czasach. Przy okazji powinieneś wyjaśnić, że nie ma funkcjonalnej różnicy między tymi dwoma instancjami.
Bergi,
12

W świetle argumentu „nazwane funkcje pojawiają się w śladach stosu”, nowoczesne silniki JavaScript są w rzeczywistości całkiem zdolne do reprezentowania funkcji anonimowych.

W tym piśmie V8, SpiderMonkey, Chakra i Nitro zawsze odnoszą się do nazwanych funkcji po ich nazwach. Niemal zawsze odnoszą się do funkcji anonimowej według jej identyfikatora, jeśli ona ma taką funkcję.

SpiderMonkey może ustalić nazwę anonimowej funkcji zwróconej z innej funkcji. Reszta nie może.

Jeśli naprawdę bardzo chciałeś, aby iterator i zwrotne informacje o sukcesie pojawiały się w śladzie, możesz też wymienić te ...

[].forEach(function iterator() {});

Ale w przeważającej części nie warto się stresować.

Uprząż ( skrzypce )

'use strict';

var a = function () {
    throw new Error();
},
    b = function b() {
        throw new Error();
    },
    c = function d() {
        throw new Error();
    },
    e = {
        f: a,
        g: b,
        h: c,
        i: function () {
            throw new Error();
        },
        j: function j() {
            throw new Error();
        },
        k: function l() {
            throw new Error();
        }
    },
    m = (function () {
        return function () {
            throw new Error();
        };
    }()),
    n = (function () {
        return function n() {
            throw new Error();
        };
    }()),
    o = (function () {
        return function p() {
            throw new Error();
        };
    }());

console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
    return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {

    try {
        func();
    } catch (error) {
        return logs.concat('func.name: ' + func.name + '\n' +
                           'Trace:\n' +
                           error.stack);
        // Need to manually log the error object in Nitro.
    }

}, []).join('\n\n'));

V8

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at e.i (http://localhost:8000/test.js:17:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: j
Trace:
Error
    at j (http://localhost:8000/test.js:20:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: l
Trace:
Error
    at l (http://localhost:8000/test.js:23:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at http://localhost:8000/test.js:28:19
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: n
Trace:
Error
    at n (http://localhost:8000/test.js:33:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: p
Trace:
Error
    at p (http://localhost:8000/test.js:38:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27 test.js:42

Pająk Małpa

func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1

Czakra

func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at e.i (http://localhost:8000/test.js:17:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at j (http://localhost:8000/test.js:20:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at l (http://localhost:8000/test.js:23:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at Anonymous function (http://localhost:8000/test.js:28:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at n (http://localhost:8000/test.js:33:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at p (http://localhost:8000/test.js:38:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)

Nitro

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
Jackson
źródło
12

W JavaScript są dwa sposoby tworzenia funkcji:

  1. Deklaracja funkcji:

    function fn(){
      console.log("Hello");
    }
    fn();

    Jest to bardzo proste, zrozumiałe, stosowane w wielu językach i standardowe w całej rodzinie języków C. Zadeklarowaliśmy funkcję, która ją zdefiniowała i wykonała, wywołując ją.

    Powinieneś wiedzieć, że funkcje w JavaScript są obiektami; wewnętrznie stworzyliśmy obiekt dla powyższej funkcji i nadaliśmy mu nazwę o nazwie fn lub odwołanie do obiektu jest przechowywane w fn. Funkcje są obiektami w JavaScript; instancja funkcji jest w rzeczywistości instancją obiektu.

  2. Wyrażenie funkcji:

    var fn=function(){
      console.log("Hello");
    }
    fn();

    JavaScript ma pierwszorzędne funkcje, to znaczy, stwórz funkcję i przypisz ją do zmiennej, tak jak tworzysz ciąg lub liczbę i przypisz ją do zmiennej. Tutaj zmienna fn jest przypisana do funkcji. Powodem tego pojęcia są funkcje w JavaScript; fn wskazuje na instancję obiektu powyższej funkcji. Zainicjowaliśmy funkcję i przypisaliśmy ją do zmiennej. Nie wykonuje funkcji i nie przypisuje wyniku.

Odwołanie: składnia deklaracji funkcji JavaScript: var fn = function () {} vs function fn () {}

Anoop Rai
źródło
1
co z trzecią opcją var fn = function fn() {...}?
chharvey
Cześć Chharvey, nie jestem pewien swojego pytania, myślę, że mówisz o wyrażeniu funkcji, o którym już wspomniałem. Jeśli jednak nadal istnieje pewne zamieszanie, po prostu bądź bardziej rozbudowany.
Anoop Rai
tak, pytałem o nazwane wyrażenie funkcji. jest podobny do opcji 2, z tą różnicą, że funkcja ma identyfikator. zwykle ten identyfikator jest taki sam jak zmienna, do której jest przypisany, ale nie zawsze tak jest.
chharvey
1
Tak Wyrażenie o nazwie funkcja jest podobne do mojej opcji # 2. Posiadanie identyfikatora nie jest obowiązkowe, ponieważ nie jest używane. Ilekroć będziesz wykonywać wyrażenie funkcyjne, użyjesz zmiennej przechowującej obiekt funkcji. Identyfikator nie służy celowi.
Anoop Rai
11

Oba są różnymi sposobami definiowania funkcji. Różnica polega na tym, jak przeglądarka interpretuje i ładuje je w kontekście wykonania.

Pierwszy przypadek dotyczy wyrażeń funkcyjnych, które ładują się tylko wtedy, gdy interpreter osiągnie ten wiersz kodu. Więc jeśli zrobisz to w następujący sposób, otrzymasz błąd, że functionOne nie jest funkcją .

functionOne();
var functionOne = function() {
    // Some code
};

Powodem jest to, że w pierwszym wierszu do funkcji Function nie przypisano żadnej wartości, a zatem jest ona niezdefiniowana. Próbujemy to nazwać funkcją, dlatego otrzymujemy błąd.

W drugim wierszu przypisujemy odwołanie funkcji anonimowej do functionOne.

Drugi przypadek dotyczy deklaracji funkcji ładowanych przed wykonaniem jakiegokolwiek kodu. Jeśli więc podoba Ci się poniższy komunikat, nie pojawi się żaden błąd, ponieważ deklaracja ładuje się przed wykonaniem kodu.

functionOne();
function functionOne() {
   // Some code
}
Nitin9791
źródło
11

O wydajności:

Nowe wersje V8wprowadziły kilka optymalizacji pod maską i tak też się stało SpiderMonkey.

Obecnie nie ma prawie żadnej różnicy między wyrażeniem a deklaracją.
Wyrażenie funkcji wydaje się teraz szybsze .

Chrome 62.0.3202 Test Chrome

FireFox 55 Test Firefox

Chrome Canary 63.0.3225 Test Chrome Canary


Anonymouswyrażenia funkcyjne wydają się mieć lepszą wydajność w stosunku do Namedwyrażeń funkcyjnych.


Firefox Chrome Canary ChromeFirefox o nazwie anonimowy Kanarek Chrome o nazwie anonimowy Chrome o nazwie anonimowy

Panos Kal.
źródło
1
Tak, ta różnica jest tak nieznaczna, że ​​mam nadzieję, że deweloperzy zastanowią się, które podejście jest łatwiejsze w utrzymaniu dla ich konkretnych potrzeb, a nie które może być szybsze (przy każdej próbie otrzymasz różne wyniki jsperf w zależności od tego, co robi przeglądarka - większość zadań javascript nie musi się w tym stopniu zajmować mikrooptymalizacjami).
squidbe
@squidbe Nie ma różnicy. Zajrzyj tutaj: jsperf.com/empty-tests-performance
Jack Giffin
10

Są dość podobne z pewnymi niewielkimi różnicami, pierwsza to zmienna przypisana do funkcji anonimowej (Deklaracja funkcji), a druga to normalny sposób tworzenia funkcji w JavaScript (Deklaracja funkcji anonimowej), zarówno pod względem użycia, wad, jak i zalet :

1. Wyrażenie funkcji

var functionOne = function() {
    // Some code
};

Wyrażenie funkcji definiuje funkcję jako część większej składni wyrażenia (zazwyczaj przypisanie zmiennej). Funkcje zdefiniowane za pomocą funkcji Wyrażenia mogą być nazwane lub anonimowe. Wyrażenia funkcyjne nie mogą zaczynać się od „funkcja” (stąd nawiasy wokół przykładu wywoływania poniżej).

Przypisanie zmiennej do funkcji oznacza brak podnoszenia, ponieważ wiemy, że funkcje w JavaScript mogą podnosić, oznacza, że ​​można je wywoływać zanim zostaną zadeklarowane, podczas gdy zmienne muszą zostać zadeklarowane przed uzyskaniem dostępu do nich, więc w tym przypadku nie możemy uzyskać dostęp do funkcji przed jej zadeklarowaniem, może to być również sposób pisania funkcji, dla funkcji, które zwracają inną funkcję, ten rodzaj deklaracji może mieć sens, również w ECMA6 i wyżej można przypisać to do funkcji strzałki, która może być używany do wywoływania funkcji anonimowych, również ten sposób deklarowania jest lepszym sposobem tworzenia funkcji konstruktora w JavaScript.

2. Deklaracja funkcji

function functionTwo() {
    // Some code
}

Deklaracja funkcji definiuje nazwaną zmienną funkcji bez konieczności przypisywania zmiennej. Deklaracje funkcji występują jako samodzielne konstrukcje i nie można ich zagnieżdżać w blokach niefunkcjonalnych. Warto myśleć o nich jak o rodzeństwie deklaracji zmiennych. Tak jak Deklaracje zmiennych muszą zaczynać się od „var”, Deklaracje funkcji muszą zaczynać się od „function”.

Jest to normalny sposób wywoływania funkcji w JavaScript. Funkcję tę można wywołać, zanim jeszcze ją zadeklarujesz, ponieważ w JavaScript wszystkie funkcje są podnoszone, ale jeśli użyjesz opcji „strict”, nie spowoduje to podniesienia zgodnie z oczekiwaniami, jest to dobry sposób wywoływać wszystkie normalne funkcje, które nie są duże w liniach i żadna z nich nie jest funkcją konstruktora.

Ponadto, jeśli potrzebujesz więcej informacji o tym, jak działa podnoszenie w JavaScript, odwiedź poniższy link:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

Alireza
źródło
1
...also this way of declaring is a better way to create Constructor functions in JavaScript, proszę rozwinąć, jestem ciekawy!
Karl Morrison,
Jednym z powodów jest to, że wszystkie wbudowane funkcje konstruktora w JavaScript utworzone, takie jak ta funkcja Number () {[kod natywny]}, nie powinny być mylone z funkcjami wbudowanymi, a także odwoływanie się później w tym przypadku jest bezpieczniejsze i koniec ładniejszy kod, ale nie używając podnoszenia ...
Alireza,
8

To tylko dwa możliwe sposoby deklarowania funkcji, a po drugie, możesz użyć funkcji przed deklaracją.

Tao
źródło
7

new Function()może być użyty do przekazania ciała funkcji w ciągu. I stąd można to wykorzystać do tworzenia funkcji dynamicznych. Również przekazywanie skryptu bez wykonywania skryptu.

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()
SuperNova
źródło
Chociaż jest to dobre i prawdziwe, jak dokładnie to samo dotyczy pytania, które jest zadawane?
Jack Giffin