Wyrażenia funkcji

gru 7, 2021

W JavaScript, funkcja nie jest „magiczną strukturą językową”, ale specjalnym rodzajem wartości.

Składnia, której używaliśmy wcześniej jest nazywana Deklaracją funkcji:

function sayHi() { alert( "Hello" );}

Istnieje inna składnia do tworzenia funkcji, która jest nazywana Wyrażeniem funkcji.

Wygląda to tak:

let sayHi = function() { alert( "Hello" );};

Tutaj funkcja jest tworzona i przypisywana do zmiennej jawnie, jak każda inna wartość. Bez względu na to, jak funkcja jest zdefiniowana, jest to po prostu wartość przechowywana w zmiennej sayHi.

Znaczenie tych próbek kodu jest takie samo: „utwórz funkcję i umieść ją w zmiennej sayHi„.

Możemy nawet wydrukować tę wartość używając alert:

function sayHi() { alert( "Hello" );}alert( sayHi ); // shows the function code

Proszę zauważyć, że ostatnia linia nie uruchamia funkcji, ponieważ nie ma nawiasów po sayHi. Istnieją języki programowania, w których każda wzmianka o nazwie funkcji powoduje jej wykonanie, ale JavaScript taki nie jest.

W JavaScript funkcja jest wartością, więc możemy się z nią obchodzić jak z wartością. Powyższy kod pokazuje jej reprezentację łańcuchową, która jest kodem źródłowym.

Oczywiście, funkcja jest specjalną wartością, w tym sensie, że możemy ją nazwać jak sayHi().

Ale nadal jest to wartość. Możemy więc pracować z nią tak samo jak z innymi rodzajami wartości.

Możemy skopiować funkcję do innej zmiennej:

function sayHi() { // (1) create alert( "Hello" );}let func = sayHi; // (2) copyfunc(); // Hello // (3) run the copy (it works)!sayHi(); // Hello // this still works too (why wouldn't it)

Oto, co dzieje się powyżej w szczegółach:

  1. Deklaracja funkcji (1) tworzy funkcję i umieszcza ją w zmiennej o nazwie sayHi.
  2. Linia (2) kopiuje ją do zmiennej func. Zwróć uwagę jeszcze raz: po sayHi nie ma nawiasów. Gdyby były, to func = sayHi() zapisywałby wynik wywołania sayHi() do func, a nie samą funkcję sayHi.
  3. Teraz funkcję można wywołać zarówno jako sayHi(), jak i func().

Zauważ, że mogliśmy również użyć Function Expression do zadeklarowania sayHi, w pierwszym wierszu:

let sayHi = function() { alert( "Hello" );};let func = sayHi;// ...

Wszystko działałoby tak samo.

Dlaczego na końcu jest średnik?

Możesz się zastanawiać, dlaczego Function Expression ma średnik ; na końcu, ale Function Declaration nie:

function sayHi() { // ...}let sayHi = function() { // ...};

Odpowiedź jest prosta:

  • Nie ma potrzeby stosowania ; na końcu bloków kodu i struktur składniowych, które ich używają, takich jak if { ... }, for { }, function f { } itp.
  • Wewnątrz instrukcji używane jest wyrażenie funkcyjne: let sayHi = ...;, jako wartość. To nie jest blok kodu, ale raczej przypisanie. Średnik ; jest zalecany na końcu instrukcji, bez względu na to, jaka jest wartość. Tak więc średnik tutaj nie jest związany z samym wyrażeniem funkcyjnym, po prostu kończy wypowiedź.

Funkcje zwrotne

Spójrzmy na więcej przykładów przekazywania funkcji jako wartości i używania wyrażeń funkcyjnych.

Piszemy funkcję ask(question, yes, no) z trzema parametrami:

question Tekst pytania yes Funkcja do uruchomienia, jeśli odpowiedź brzmi „Tak” no Funkcja do uruchomienia, jeśli odpowiedź brzmi „Nie”

Funkcja powinna zadać pytanie question i, w zależności od odpowiedzi użytkownika, wywołać yes() lub no():

function ask(question, yes, no) { if (confirm(question)) yes() else no();}function showOk() { alert( "You agreed." );}function showCancel() { alert( "You canceled the execution." );}// usage: functions showOk, showCancel are passed as arguments to askask("Do you agree?", showOk, showCancel);

W praktyce takie funkcje są całkiem przydatne. Główna różnica między prawdziwym ask a powyższym przykładem polega na tym, że prawdziwe funkcje wykorzystują bardziej złożone sposoby interakcji z użytkownikiem niż prosty confirm. W przeglądarce, taka funkcja zazwyczaj rysuje ładnie wyglądające okienko z pytaniem. Ale to już inna historia.

Argumenty showOk i showCancel z ask nazywane są funkcjami wywołania zwrotnego lub po prostu callbackami.

Pomysł polega na tym, że przekazujemy funkcję i oczekujemy, że zostanie ona „wywołana z powrotem” później, jeśli zajdzie taka potrzeba. W naszym przypadku, showOk staje się callbackiem dla odpowiedzi „tak”, a showCancel dla odpowiedzi „nie”.

Możemy użyć Function Expressions, aby napisać tę samą funkcję znacznie krócej:

function ask(question, yes, no) { if (confirm(question)) yes() else no();}ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); });

Tutaj funkcje są zadeklarowane wewnątrz wywołania ask(...). Nie mają one żadnej nazwy, a więc są nazywane anonimowymi. Takie funkcje nie są dostępne poza ask (ponieważ nie są przypisane do zmiennych), ale właśnie o to nam tutaj chodzi.

Taki kod pojawia się w naszych skryptach bardzo naturalnie, jest to zgodne z duchem JavaScriptu.

Funkcja jest wartością reprezentującą „akcję”

Regularne wartości, takie jak łańcuchy lub liczby reprezentują dane.

Funkcja może być postrzegana jako akcja.

Możemy ją przekazać między zmiennymi i uruchomić kiedy chcemy.

Function Expression vs Function Declaration

Sformułujmy kluczowe różnice między deklaracjami funkcji a wyrażeniami.

Po pierwsze, składnia: jak je rozróżnić w kodzie.

  • Deklaracja funkcji: funkcja, zadeklarowana jako oddzielna deklaracja, w głównym strumieniu kodu.

    // Function Declarationfunction sum(a, b) { return a + b;}

  • Wyrażenie funkcji: funkcja, utworzona wewnątrz wyrażenia lub wewnątrz innej konstrukcji składniowej. Tutaj funkcja jest tworzona po prawej stronie „wyrażenia przypisania” =:

    // Function Expressionlet sum = function(a, b) { return a + b;};

Subtelniejszą różnicą jest to, kiedy funkcja jest tworzona przez silnik JavaScript.

Wyrażenie funkcyjne jest tworzone, kiedy wykonanie go osiąga i jest użyteczne tylko od tego momentu.

Gdy strumień wykonania przejdzie na prawą stronę przypisania let sum = function… – oto idziemy, funkcja jest tworzona i może być używana (przypisywana, wywoływana, itp.) od tego momentu.

Deklaracje funkcji są inne.

Deklaracja funkcji może być wywołana wcześniej niż jest zdefiniowana.

Na przykład, globalna deklaracja funkcji jest widoczna w całym skrypcie, bez względu na to, gdzie się znajduje.

To wynika z wewnętrznych algorytmów. Kiedy JavaScript przygotowuje się do uruchomienia skryptu, najpierw szuka w nim globalnych Deklaracji Funkcji i tworzy te funkcje. Możemy myśleć o tym jako o „etapie inicjalizacji”.

A po przetworzeniu wszystkich deklaracji funkcji, kod jest wykonywany. Ma on więc dostęp do tych funkcji.

Na przykład działa to tak:

sayHi("John"); // Hello, Johnfunction sayHi(name) { alert( `Hello, ${name}` );}

Deklaracja funkcji sayHi jest tworzona, gdy JavaScript przygotowuje się do uruchomienia skryptu i jest widoczna wszędzie w nim.

…Gdyby to było wyrażenie funkcyjne, to nie działałoby:

sayHi("John"); // error!let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` );};

Wyrażenia funkcyjne są tworzone, gdy wykonanie do nich dotrze. Tak stałoby się tylko w linii (*). Za późno.

Inną specjalną cechą deklaracji funkcji jest ich zakres blokowy.

W trybie ścisłym, gdy deklaracja funkcji znajduje się wewnątrz bloku kodu, jest widoczna wszędzie wewnątrz tego bloku. Ale nie poza nim.

Na przykład, wyobraźmy sobie, że musimy zadeklarować funkcję welcome() w zależności od zmiennej age, którą otrzymujemy podczas runtime. A następnie planujemy użyć jej jakiś czas później.

Jeśli użyjemy Function Declaration, nie będzie ona działać zgodnie z przeznaczeniem:

let age = prompt("What is your age?", 18);// conditionally declare a functionif (age < 18) { function welcome() { alert("Hello!"); }} else { function welcome() { alert("Greetings!"); }}// ...use it laterwelcome(); // Error: welcome is not defined

To dlatego, że Function Declaration jest widoczna tylko wewnątrz bloku kodu, w którym rezyduje.

Oto kolejny przykład:

let age = 16; // take 16 as an exampleif (age < 18) { welcome(); // \ (runs) // | function welcome() { // | alert("Hello!"); // | Function Declaration is available } // | everywhere in the block where it's declared // | welcome(); // / (runs)} else { function welcome() { alert("Greetings!"); }}// Here we're out of curly braces,// so we can not see Function Declarations made inside of them.welcome(); // Error: welcome is not defined

Co możemy zrobić, aby welcome był widoczny poza if?

Poprawnym podejściem byłoby użycie Wyrażenia funkcyjnego i przypisanie welcome do zmiennej, która jest zadeklarowana poza if i ma odpowiednią widoczność.

Ten kod działa zgodnie z przeznaczeniem:

let age = prompt("What is your age?", 18);let welcome;if (age < 18) { welcome = function() { alert("Hello!"); };} else { welcome = function() { alert("Greetings!"); };}welcome(); // ok now

Albo moglibyśmy uprościć go jeszcze bardziej, używając operatora znaku zapytania ?:

let age = prompt("What is your age?", 18);let welcome = (age < 18) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); };welcome(); // ok now

Kiedy wybrać deklarację funkcji vs. wyrażenie funkcji?

Jako zasada kciuka, kiedy musimy zadeklarować funkcję, pierwszą do rozważenia jest składnia Function Declaration. Daje ona więcej swobody w organizacji naszego kodu, ponieważ możemy wywoływać takie funkcje zanim zostaną zadeklarowane.

Jest to również lepsze dla czytelności, ponieważ łatwiej jest szukać function f(…) {…} w kodzie niż let f = function(…) {…};. Deklaracje funkcji są bardziej „przyciągające wzrok”.

…Ale jeśli deklaracja funkcji z jakiegoś powodu nam nie odpowiada, lub potrzebujemy deklaracji warunkowej (właśnie widzieliśmy przykład), to należy użyć Function Expression.

Podsumowanie

  • Funkcje są wartościami. Mogą być przypisane, skopiowane lub zadeklarowane w dowolnym miejscu kodu.
  • Jeśli funkcja jest zadeklarowana jako oddzielna instrukcja w głównym strumieniu kodu, to jest to nazywane „Deklaracją funkcji”.
  • Jeśli funkcja jest utworzona jako część wyrażenia, to jest to nazywane „Wyrażeniem funkcji”.
  • Deklaracje funkcji są przetwarzane przed wykonaniem bloku kodu. Są one widoczne wszędzie w bloku.
  • Wyrażenia funkcyjne są tworzone, gdy przepływ wykonania do nich dotrze.

W większości przypadków, gdy potrzebujemy zadeklarować funkcję, deklaracja funkcji jest lepsza, ponieważ jest ona widoczna przed samą deklaracją. To daje nam większą elastyczność w organizacji kodu i zazwyczaj jest bardziej czytelne.

Więc powinniśmy używać Function Expression tylko wtedy, gdy Function Declaration nie nadaje się do tego zadania. Widzieliśmy już kilka takich przykładów w tym rozdziale, a w przyszłości zobaczymy ich jeszcze więcej.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.