Kurs JavaScript
Co to jest JavaScript?
JavaScript to język programowania, który powstał w 1995 roku jako prosty kliencki język skryptowy dla przeglądarki Netscape Navigator, ale przeistoczył się w jeden z głównych filarów sieci Sieci Światowej (ang. World Wide Web) (razem z HTML oraz CSS), którego wykorzystanie rozszerzyło się na stronę serwerową (przede wszystkim ze względu na powstanie środowiska uruchomieniowego używanego po stronie serwera Node.js). Pomimo tego, że JavaScript zaczął jako przedsięwzięcie jednej firmy, teraz jest powszechnie używany, większość głównych przegląderek ma wbudowane śrowiska uruchomieniowe JavaScript, a Ecma International standaryzuje jego wersje.
Wprowadzenie
Z punktu widzenia technologicznego, JavaScript jest językiem programowania, który:
- ma składnię zbliżoną do C,
- jest wysokopoziomowy (ang. high-level),
- jest kompilowany na bieżąco (ang. just-in-time compiled),
- jest słabo-typowany (ang. weakly typed),
- jest dynamicznie typowany (ang. dynamically typed),
- zawiera dziedziczenie oparte na prototypach (ang. prototype-based inheritance), inaczej zwane orientacją prototypową,
- zawiera dziedziczenie oparte na klasach (ang. class-based inheritance), inaczej zwane orientacją klasową, oraz
- wspiera wiele paradygmatów programowania (ang. multi-paradigm programming), włączając w to programowanie obiektowe (ang. object-oriented), funkcyjne (ang. functional), imperatywne (ang. imperative), oraz sterowane zdarzeniami (ang. event-driven).
Początki JavaScript
W 1993 roku, przeglądarka NCSA Mosaic, utworzona w National Center for Supercomputing Applications przy Uniwersytecie Illinois, została wydana. Stała się pierwszą powszechnie używaną przeglądarką dostępną na systemie operacyjnym Windows, która integrowała tekst i grafikę. Wspierała wiele protokołów internetowych takich jak HTTP, czyli Protokół Przesyłania Dokumentów Hipertekstowych (ang. Hypertext Transfer Protocol) oraz FTP, czyli Protokół Przeysłania Plików (ang. File Transfer Protocol), ale nie zawierała języka skryptowego podobnego do JavaScript. Microsoft wylicencjował jedną z wersji Mosaic w 1995 roku i zbudował Internet Explorer na jej podstawie.
W 1994 roku, główny deweloper Mosaic utworzył przeglądarkę Netscape Navigator, która szybko stała się popularna. W roku 1995, zdecydowano się dodać język skryptowy do Netscape Navigator, aby umożliwić dynamiczną modyfikację dokumentów. Jednym z pomysłów było użycie języka Java we współpracy z Sun Microsystems, innym wbudowanie języka Schema. Jednakże, finalnie, zdecydowano się na utworzenie nowego. Zadanie to zostało powierzone Brendanowi Eich.
Nowo-powstały język skryptowy nazwany został LiveScript w jednej z beta iteracji przeglądarki Netscape, ale nazwa została zmieniona na JavaScript w oficjalnym wydaniu. Pierwsza oficjalna wersja Netscape Navigator, która zawierała JavaScript została wydana w 1995 roku.
Microsoft również wydał swoją przeglądarkę, Internet Explorer, w roku 1995. Internet Explorer zawierał JScript, którego program interpretujący (ang. interpreter) był poddany inżynierii odwrotnej (ang. reverse-engineered) z Netscape Navigator. Tym samym marsz do powszechności JavaScript jako głównego języka skryptowego sieci rozpoczął się.
Standaryzacja JavaScript przez Ecma
Około jeden rok po oficjalnym debiucie, Netscape przedłożył JavaScript do Ecma Inernational do standaryzacji.
Pierwsza wersja JavaScript standaryzowana przez Ecma International nazwana została ECMAScript (ES1) i została wydana w roku 1997.
Druga standaryzowana wersja JavaScript, nazwana ECMAScript 2 (ES2), została wydana w roku 1998.
Trzecia standaryzowana wersja JavaScript, nazwana ECMAScript 3 (ES3) została wydana w roku 1999.
Początkowo, Microsoft oraz jego przeglądarka Internet Explorer stosowali się do standaryzacji Ecma, ale później jego współpraca ustała. Z uwagi na przeważający ówczesny udział w rynku przeglądarek Internet Explorer, spowodowało to, że czwarta wersja standaryzowanego JavaScript (ES4) nigdy nie została wydana.
Dominacja Microsoft na rynku przeglądarek wczesnych lat dwutysięczncyh została złamana wydaniem w roku 2004 następcy Netscape Navigator'a, Firefox, oraz wydaniem w roku 2008 Google Chrome. Google Chrome zawierał szybki silnik uruchomieniowy (ang. runtime engine) - V8. Obie przeglądrki przejęły duże części rynku. Potrzeba standaryzacji pojawiła się na nowo.
Wspólny wysiłek zainteresowanych stron oraz Ecma International doprowadził do wydania epokowych wersji JavaScript, a mianowicie ECMAScript 5 (ES5) w roku 2009 oraz ECMAScript 6 (ES6) w roku 2015.
Kolejne wersje JavaScript oznaczane są raczej rokiem wydania, niż numerem. Dlatego, po ES6, wersje ECMAScript nazwane zostały ECMAScript 2016, ECMAScript 2017, ECMAScript 2018, ECMAScript 2019, etc. Jednakże, numeryczne wersje, ES7, ES8, ES9, ES10, również czasem są używane.
JavaScript jako kliencki język programowania
Jak nakreślono powyżej, JavaScript powstał jako język programowania wbudowany w program kliencki (ang. user-agent), w przeglądarkę.
Dziś, JavaScript jest głównym i najbardziej rozpowszechnionym klienckim językiem programowania Sieci Światowej (ang. World Wide Web).
Po stronie klienta JavaScript może być używany między innymi do:
- manipulowania Obiektowym Modelem Dokumentu (ang. Document Object Model), to jest do dodawania, zmiany, oraz usuwania elementów dokumentów w przeglądarkach, oraz
- pobierania danych ze zdalnych hostów (komputerów).
Aby ułatwić tworzenie oprogramowania w JavaScript po stronie klienta powstało wiele bibliotek i frameworków. Obecnie, jedne z najpopularniejszych z nich to:
JavaScript jako serwerowy język programowania
Pomimo tego, że JavaScript rozpoczął swoje życie jako kliencki (wbudowany w przeglądarkę) język skryptowy, to obecnie jest również powszechnie używany jako język serwerowy
Popularność JavaScript jako języka serwerowego rozpoczęła się wraz z utworzeniem Node.js w roku 2009 przez Ryan Dahl'a.
Node.js używa pod spodem silnika JavaScript V8 stworzonego przez Google. Node.js zawiera pętle wydarzeń (ang. event loop) oraz interfejsy I/O (wejścia i wyjścia).
Domyślny menadżer paczek dla Node.js zwany jest npm.
Jest wiele serwerowych bibliotek i frameworków JavaScript, które ułatwiają tworzenie programowania. Niektóre godne uwagi, to:
JavaScript jako wysokopoziomowy język programowania
JavaScript jest wysokopoziomowym językiem programowania (ang. high-level programming language).
Wysokopoziomowy język programowania to język programowania, który nie wymaga bezpośredniego zarządzania sprzętem komputerowym (np. zarządzania pamięcią), ale jest zdolny do automatyzacji takiego zarządzania, gdy jest taka potrzeba, poprzez wbudowane procedury, i, tym samym, umożliwia łatwiejsze i szybsze tworzenie oprogramowania.
JavaScript jako kompilowany na bieżąco język programowania
JavaScript jest językiem programowania kompilowanym na bieżąco (ang. just-in-time compiled).
Kompilowany na bieżąco język programowania to język programowania, w którym kod źródłowy jest kompilowany do kodu maszynowego w częściach podczas wykonywania tych części, zamiast stosowania kompilacji poprzedzającej (ang. ahead-of-time compilation). Ogólnie, kompilacja na bieżąco łączy szybkość kompilacji poprzedzającej z elastycznością interpretacji.
JavaScript jako słabo-typowany język programowania
JavaScript jest często opisywany jako słabo-typowany (ang. weakly typed) język programowania.
Jednakże, istnieje wiele różnych definicji czym słabo-typowany język programowania rzeczywiście jest.
Gdy opisywany jest JavaScript jako słabo-typowany język, to przez to często rozumiane są zdolności JavaScript do domyślnej zmiany typów (ang. implicit casting) swych wartości w niektórych ze swoich wbudowanych operatorów.
Na przykład, gdy porównujemy dwie wartości o różnych typach danych używając ==, czyli operatora porównywania abstrakcyjnego (ang. abstract comparison operator), to typy tych wartości są domyślnie zmieniane na spójne przed samym porównaniem.
1 == "1" // => trueInnym przykładem jest domyślna zmiana typu obu operandów na słowo (ang. string), gdy którykolwiek z tych operandów jest liczbą, przez operator binarny +.
1 + "1" // => "11"Jeszcze innym przykładem jest domyślna zmiana typów obu operandów na liczbę (ang. number), gdy którykolwiek z tych operandów nie jest liczbą, przez operator binarny -.
1 - "1" // => 0JavaScript jako dynamicznie typowany język programowania
JavaScript jest dynamicznie typowanym (ang. dynamically typed) językiem programowania.
Dynamicznie typowany język programowania to język programowania, w którym typ identyfikatora (takiego jak zmienna) jest wywodzony przez interpretator podczas wykonywania i może się dynamicznie zmieniać podczas tego wykonywania, w przeciwieństwie do bycia statycznie ustalonym (wprost lub w sposób dorozumiany) przed komiplacją poprzedzającą.
// JavaScript (dynamiczne typowanie)
let foo = 42
foo = "czterdzieści dwa" // Zmiana z liczby na słowo dozwolona.// TypeScript (statyczne typowanie)
let foo = 42 // typ foo statycznie w sposób dorozumiany ustalony jak 'liczba'.
foo = "czterdzieści dwa" // Typ 'słowo' nie może zostać przypisany do typu 'liczba'.JavaScript jako język programowania oparty na prototypach
Od swojego początku w roku 1995, JavaScript jest językiem programowania opartym na prototypach.
Język programowania oparty na prototypach to obiektowy język programowania, w którym obiekt może dziedziczyć właściwości (ang. properties) drugiego obiektu poprzez a) ustawienie tego drugiego obiektu jako prototypu oraz delegowanie wezwań tych właściwości do tego drugiego obiektu (prototypowanie oparte na delegacji), lub b) kopiowanie właściwości tego drugiego obiektu (prototypowanie konkatenacyjne), oraz w przeciwieństwie do dziedziczenia tych właściwości przez instancjanowanie specjalnie dedykowanych konstruków zwanych klasami.
JavaScript implementuje prototypowanie oparte na delegacji.
const czlowiek = { zawod: 'pracowanie', rzad: 'ssaki naczelne' }
const student = { imie: 'Dawid', zawod: 'studiowanie' }
Object.setPrototypeOf(student, czlowiek)
student.imie // => 'Dawid'
student.zawod // => 'studiowanie'
student.rzad // => 'ssaki naczelne'Czytaj dalej w Dziedziczenie oparte na prototypach w JavaScript.
JavaScript jako język programowania oparty na klasach
Począwszy od ECMAScript 2015 (ES6), JavaScript jest językiem programowania opartym na klasach.
Język programowania oparty na klasach to obiektowy język programowania, w którym obiekt może dziedziczyć właściwości od szczególnie dedydkowanego dla dziedziczenia konstruktu zwanego klasą (która sama może, ale nie musi, być obiektem), i w przeciwieństwie do dziedziczenia właściwości od obiektu niebędącego klasą.
class Pies {
constructor(imie) {
this.imie = imie
}
szczekaj() {
console.log('Hau!')
}
}
const pluto = new Pies('Pluto')
pluto.imie // => 'Pluto'
pluto.szczekaj() // 'Woof!'Czytaj dalej w Dziedziczenie oparte na klasach w JavaScript.
JavaScript jako obiektowy język programowania
JavaScript jest obiektowym językiem programowania.
Obiektowy język programowania to język programowania, który zawiera obiekty, czyli konstrukty programistyczne, które a) mają unikalną tożsamość, b) zawierają dane (często wspólnie zwane stanem) oraz procedury (często zwane metodami), które łacznie określa się właściwościami (ang. properties), oraz które c) mogą dziedziczyć takie właściwości od szczególnie dedykowanych dziedziczeniu konstruktów zwanych klasami (które mogą, ale nie muszą, same być obiektami) lub od innych obiektów nieklasowych, które zwane są wtedy prototypami.
const pirat = {
imie: 'Hook',
okrzyk: function() { console.log('Huzzah!') }
}
pirat.imie // => 'Hook'
pirat.okrzyk() // 'Huzzah!'Jednakże, JavaScript nie jest obiektowym językiem programowania w pełni (tak jak jest np. Ruby), gdyż wciąż zawiera wartości pierwotne (ang. primitive values), takie jak liczby (ang. numbers), słowa (ang. strings), oraz wartości logiczne (ang. booleans), które nie mają stanu, ani metod z nimi powiązanych.
JavaScript jako funkcyjny język programowania
JavaScript jest funkcyjnym językiem programowania.
Funkcyjny język programowania jest językiem programowania, w którym funkcje mogą być przypisywane do identyfikatorów, takich jak zmienne, przekazywane jako argumenty innych funkcji, oraz zwracane jako wartości z innych funkcji.
Gdy funkcje mają zdolności do bycia przypisywanym do identyfikatorów, do bycia przekazywanym jako argumenty, oraz bycia wartościami zwrotnymi innych funkcji, to często określa się je obywatelami pierwszej klasy (ang. first-class citizens).
JavaScript może wykorzystywać dwa wzorce programowania funkcyjnego:
- nieczyste programowanie funkcyjne, oraz
- czyste programowanie funkcyjne.
Nieczyste programowanie funkcyjne to programowanie funkcyjne:
- które może polegać na wartościach spoza zakresu (ang. scope) funkcji i tym samym zwracać różne wartości, gdy podane są te same argumenty, oraz,
- w których wykonanie funkcji może mieć efekty uboczne (ang. side effects) takie jak modyfikacje wartości spoza zakresu tej funkcji.
Czyste programowanie funkcyjne (ang. pure functional programming) to programowanie funkcyjne:
- które nie może polegać na wartościach spoza zakresu (ang. out-of-scope) funkcji i tym samym zawsze zwraca te same wartości przy podaniu tych samych argumentów,
- w których wykonanie funkcji nie może mieć efektów ubocznych takich jak modyfikacja wartości spoza zakresu tej funkcji.
JavaScript jako imperatywny język programowania
JavaScript może być używany jako imperatywny język programowania.
Imperatywny język programowania to język programowania, który używa sekwencji rozkazów (ang. commands) - w Łacinie imperare znaczy rozkazywać - aka instrukcji (ang. statements), z których każdy wskazuje szczególne działanie, które ma zostać podjęte, i które może bezpośrednio zmienić stan danego procesu.
let wiek = 42
const czujeSieMlodo = true
if (czujeSieMlodo) {
wiek = wiek / 2
}JavaScript jako sterowany wydarzeniami język programowania
JavaScript może być używany jako sterowany wydarzeniami (ang. event driven) język programowania.
Sterowany wydarzeniami (ang. event driven) język programowania to język programowania, w którym kontrola przepływu (ang. control flow) procesu danego programu determinowana jest wydarzeniami uruchamianymi przez a) komunikaty (ang. messages) z samego procesu, b) komunikaty z innych procesów, lub c) interakcje użytkownika (naciśnięcia klawiszy, kliknięcia myszy, przewijanie myszą, etc.).
// Loguje "kliknięto okno!" za każdym razem, gdy okno przeglądarki jest kliknięte.
const zalogujKlinieteOkno = () => console.log('kliknięto okno!')
window.addEventListener('click', zalogujKlinieteOkno, false)Zmienne
Var
Przed ES6 sposobem deklaracji zmiennej było używanie słowa kluczowego var.
var foo = 'bar'Nie tylko referencja zmiennej var może ulec zmianie, ale nawet sama zmienna moż być ponownie zadeklarowana.
var foo = 'bar'
foo = "rab"
foo // => "rab"
var foo = 42
foo // => 42Funkcyjny zakres Var
Zmienne deklarowane przy użyciu słowa kluczowego var mają zakres funckji, a więc - chyba, że są zadeklarowane w zakresie globalnym - są wyłącznie dostępne w zakresie funkcji, w ramach, której zostały zadeklarowane.
var foo = 'bar'
function logFoo() {
var foo = 'rab'
console.log(foo)
}
console.log(logFoo()) // "rab"
console.log(foo) // "bar"
if (true) {
var foo = '42'
}
console.log(foo) // 42Podciąganie (ang. hoisting) Var
Zmienne var - niezależnie od tego w jakim zakresie zostały zadeklarowane - są podciągane (ang. hoisted) przed wykonaniem codu, tzn. przenoszone na początek zakresu i inicjalizowane z wartością undefined.
function logVar() {
console.log(foo)
var foo = 'bar'
console.log(foo)
}
logVar() // undefined "bar"Let
W dodatku do var ES6 wprowadził słowa kluczowe let oraz const. let umożliwia definiowanie zmiennych, a const stałych.
Wartość referowana przez obie - zmienną var oraz zmienną let - może ulec zmianie, ale zmienna let - w przeciwieństwie do zmiennej var - nie może być ponownie zadeklarowana.
let foo = 'bar'
foo = 'rab'
foo // => "rab"
let foo = 42 // Uncaught SyntaxError: Identifier 'foo' has already been declaredBlokowe (aka leksykalne, aka statyczne) zakresowanie let
Zmienna zadeklarowana z let nie ma zakresu funkcyjnego, tak jak zmienna zadeklarowana z var, a zakres blokowy. Oznacza to, że dla zmiennych zadeklarowanych z let istnieje nie tylko oddzielny zakres globalny, oddzielny zakres funkcyjny, ale również oddzielny zakres w ramach danego bloku.
let foo = 'bar'
if (true) {
let foo = 'rab'
}
console.log(foo) // "bar"Const
ES6 wprowadziło możliwość deklarowania stałych używając słowa kluczowego const.
Nie tylko stałe nie mogą zostać ponownie zadeklarowane, ale również ich referencje nie mogą się zmienić. Pomimo tego, że referencja raz zadeklarowanej stałej nie może się zmienić, to nie oznacza to, że referowana wartość nie może ulec zmianie.
const foo = 'bar'
foo = 'rab' // Uncaught TypeError: Assignment to constant variable.
const foobar = { a: 1 }
foobar.a = 2
foobar // { a: 2 }Stałe - tak jak zmienne deklarowane przy użyciu let - mają zakres leksykalny.
Prymitywne typy danych
6 + 1 prymitywnych typów danych w JavaScript?
W ES6, czyli wersji JavaScript z 2015 roku, jest sześć prymitywnych typów danych:
undefined,boolean,number,string,bigint, orazsymbol.
Ponadto, specjalnym typem prymitywnym jest null, czyli Rdzenna Strukturalna Wartość Prymitywna (ang. Structural Root Primitive).
Niezmienność (ang. immutability)
Wszystkie prymitywne typy danych w JavaScript są niezmienne.
Sprawdzanie typów
Aby sprawdzić jaki dana wartość ma typ używamy operatora typeof.
typeof undefined === 'undefined' // => true
typeof true === 'boolean' // => true
typeof 1 === 'number' // => true
typeof '1' === 'string' // true
typeof BigInt(4242424242424242424242424242424) === 'bigint' // => true
typeof Symbol() === 'symbol' // => true
typeof null === 'object' // trueundefined
undefined używane jest, aby wyrazić, że referowana wartość nie istnieje. Na przykład, zmienna, która została zadeklarowana, ale nie została przypisana do niej żadna wartość, ma wartość undefined przypisaną automatycznie.
boolean
W JavaScript są dwa typy boolowskie (ang. boolean), czyli logiczne: true oraz false.
number
Typ danych number może oznaczać zarówno liczbę całkowitą (ang. an integer), jak i liczbę zmiennoprzecinkową (ang. a floating-point number).
Zakres liczb całkowitych, które typ danych number może reprezentować zaczyna się od -(2^53 − 1), a kończy na 2^53 − 1. Aby sprawdzić czy dana liczba całkowita może być bezpiecznie reprezentowana przez typ danych number można użyć metody Number.isSafeInteger(). Natomiast, jeżeli dana liczba całkowita wykraczas poza podany zakres, to reprezentowana jest przez podwójnie precyzyjną (ang. double-precision) przybliżoną liczbę zmiennoprzecinkową. Problem ten rozwiązuje typ danych bigint.
bigint
Typ danych bigint może być użyty do reprezentoania liczb całkowitych z dowolną precyzją, która wybiega poza bezpieczny zakres typu danych number. Typ danych bigint tworzony jest poprzez dołączanie n na końcu danej liczby całkowitej lub poprzez użycie konstruktora BigInt.
typeof 9007199254740992n // => "bigint"
typeof BigInt(9007199254740992) // => "bigint"string
W JavaScript typ danych string, czyli ciąg (znaków), dedykowany jest reprezentowaniu danych tekstowych. W JavaScript string to uporządkowany ciąg znaków (ang. characters), które z kolei są wizualną reprezentacją 16-bitowych liczb całkowitych bez znaku (ang. 16-bit unsigned integers). Indeks pierwszego znaku w danym string'u to 0.
Stringi w JavaScript są niezmienne (ang. immutable).
// `String.substr()` jest używane do uzyskania części stringa.
'abcdef'.substr(1,3) // => "bcd"
// W celu łączenia dwóch stringów używa się metody `String.concat()`.
'abc'.concat('def') // => "abcdef"symbol
Prymitywny typ danych symbol jest nowością wprowadzoną do JavaScript w wersji ES6 (aka ES2015). symbol w JavaScript jest anonimową, unikalną wartością, identyfikatorem, który może być używany jako właściwość (ang. property) obiektu.
W celu utworzenia symbolu używa się konstruktora Symbol, do którego można podać argument z opisem (ang. description). Utworzenie dwóch symboli z tym samym opisem nie oznacza, że oba symbole są sobie równoważne. Każdy symbol w JavaScript jest unikalny.
Symbol('foo') === Symbol('foo') // => false
const symA = Symbol()
const symB = Symbol()
const anObj = {
[symA]: 1,
[symB]: 2
}
console.log(anObj) // {Symbol(): 1, Symbol(): 2}
anObj[symA] // => 1
anObj[symB] // => 2Właściwości obiektów, które są symbolami, są ignorowane przez JSON.stringify.
null
null jest typem danych oznaczającym, że choć referowana wartość istnieje, to jest ona pustką (ang. emptiness). null jest często używany jako wartość właściwości obiektów, które obecnie są puste (choć nie są undefined), ale w przyszłości oczekuje się, że takie nie będą.
foo // Uncaught ReferenceError: foo is not defined
let foo // => undefined
foo = null // => nullFunkcje
Co to są funkcje w JavaScript?
Funkcja w JavaScript to potencjalnie reużywalny zestaw wyrażeń (ang. expressions) oraz instrukcji (ang. statements), który akceptuje zero lub więcej parametrów i może zwracać wartość.
W JavaScript funkcja może zostać utworzona poprzez:
- deklarację funkcji lub
- wyrażenie funkcji.
Deklaracja funkcji
Deklaracja funkcji to utworzenie funkcji i powiązanie tej nowo-utworzonej funkcji z identyfikatorem, do którego można odwoływać się poza zakresem tej funkcji.
Deklaracja funkcji polega:
- użyciu słowa kluczowego
functionz dodanym wymaganym identyfikatorem, stanowiącym nazwę tej funkcji, - dodaniu zero, jednego lub więcej parametrów zawartych w nawiasach i rozdzielonych przecinkami, oraz
- dodaniu bloku kodu zawartego w nawiasach klamrowych, ale
- nieprzypisywaniu tak utworzonej funkcji do zmiennej, ani
- nieprzypisywaniu tak utworzonej funkcji do właściwości obiektu, ani
- niezawieraniu tak utworzonej funkcji w nawiasach.
function dodaj(a, b) {
return a + b
}
dodaj(2, 2) // => 4
// Deklaracja funkcji wiąże tę funkcję z identyfikatorem.
const dodaj = 'foo' // Uncaught SyntaxError: Identifier 'dodaj' has already been declaredWyrażenie funkcji
Wyrażenie funkcji to utworzenie funkcji bez wiązania tej nowo-utworzonej funkcji z identyfikatorem, do którego możnaby się odwoływać poza zakresem tej funkcji.
Wyrażenie funkcji może nastąpić poprzez:
- użycie słowa kluczowego
functioni spełnienie określonych warunków, albo - użycie składni strzałkowej (arrow syntax).
Wyrażenie funkcji z użyciem słowa kluczowego function
Wyrażenie funkcji przy użyciu słowa kluczowego function polega na:
- użyciu wspomnianego słowa kluczowego
functionwraz z opcjonalnym identyfikatorem, stanowiącym nazwę tej funkcji, - dodaniu zero, jednego lub więcej parametrów zawartych w nawiasach i rozdzielonych przecinkami, oraz
- dodaniu bloku kodu zawartego w nawiasach klamrowych, przy jednoczesnym
- przypisaniu tak utworzonej funkcji do zmiennej, lub
- przypisywaniu tak utworzonej funkcji do właściwości obiektu, lub
- zawarciu tak utworzonej funkcji w nawiasach.
Wyrażenie funkcji polegające na utworzoniu funkcji wraz z przypisaniem takiej funkcji do zmiennej:
const foo = function odejmij(a, b) {
return a - b
}
// Nie następuje utworzenie powiązania funkcji z identyfikatorem 'odejmij'.
odejmij(4, 2) // Uncaught ReferenceError: odejmij is not defined
// Następuje przypisanie funkcji do zmiennej 'foo'.
foo(4, 2) // => 2Wyrażenie funkcji polegające na utworzeniu funkcji wraz z przypisaniem takiej funkcji do właściwości obiektu:
const foo = {
bar: function odejmij(a, b) {
return a - b
}
}
// Nie następuje utworzenie powiązania funkcji z identyfikatorem 'odejmij'.
odejmij(4, 2) // Uncaught ReferenceError: odejmij is not defined
// Następuje przypisanie funkcji do właściwości `bar` obiektu 'foo'.
foo.bar(4, 2) // => 2Wyrażenie funkcji polegające na utworzeniu funkcji wraz z zawarciem takiej funkcji w nawiasach.
(function odejmij(a, b) {
return a - b
})
// Nie następuje utworzenie powiązania funkcji z identyfikatorem 'odejmij'.
odejmij(4, 2)Wyrażenie funkcji używające słowa kluczowego function, może, ale nie musi zawierać nazwy funkcji. Wyrażone funkcje bez nazwy nazywane są funkcjami anonimowymi.
(function(a, b) {
return a + b
})(2, 2) // => 4
const dodaj = function(a, b) { return a + b }
dodaj(2, 2) // => 4Wyrażenie funkcji z użyciem składni strzałkowej
Innym sposobem na wyrażenie funkcji, niż użycie słowa kluczowego function, jest użycie funkcji strzałkowej (arrow function), która to możliwość wprowadzona została przez ES6.
Funkcje strzałkowe nie mogą mieć przypisanej nazwy, a więc są zawsze anonimowe.
const dodaj = (a, b) => { return a + b }
dodaj(2, 2) // => 4Nawiasy klamrowe oraz słowo kluczowe return mogą być pominięte w jednolinijkowych funkcjach strzałkowych, a wtedy funkcja ta zwraca wyrażenie zawarte w jej ciele.
const dodaj = (a, b) => a + b
dodaj(2, 2) // => 4Parametryzacja funkcji
Funkcje w JavaScript mogą przyjmować zero, jeden lub więcej parametrów.
Parametry mogą być użyte w ciele funkcji i wpływać na wyrażenia i stwierdzenia zawarte w ciele funkcji, a tym samym na zwracaną wartość oraz efekty uboczne wykonywane przez tę funkcję.
Wpływ parametrów na zwracaną wartość:
function dodaj(a,b) {
return a + b
}
dodaj(2,2) // => 4
dodaj(2,3) // => 5
dodaj(3,3) // => 6Wpływ parametrów na efekty uboczne:
const foo = { bar: 'baz' }
function zmienObiekt(o, n) {
o.bar = n
}
zmienObiekt(foo, 'qux')
foo // => { bar: 'qux' }Parametry vs argumenty
Przyjmuje się, że nazwy znajdujące się w nawiasach podczas tworzenia funkcji określa się parametrami. Podczas, gdy rzeczywiste wartości podawane przy wywoływaniu funkcji nazywa się argumentami. Vide JavaScript Function Parameters.
function dodaj(a, b) { // a oraz b to parametry.
return a + b
}
dodaj(2, 3) // 2 oraz 3 to argumenty.Przekazywanie argumentów przez wartość
Wartości o prymitywnych typach danych, takich jak na przykład number, string lub boolean, gdy podawane są do funkcji w trakcie jej wywoływania jako argumenty, przekazywane są przez wartość a nie jako kopie.
Z kolei wartości będące obiektami, gdy podawane są do wywoływanej funkcji jako argumenty, mają referencje do samych siebie skopiowane a następnie te kopie referencji są przekazywane do tej funkcji. Oznacza to, że obiekty, gdy są podawane do funkcji jako argumenty, nie są przekazywane jako kopie (tylko referencje do nich są kopiowane), a również przez wartość.
Oznacza to, że przypisanie nowej wartości do argumentu funkcji nie ma wpływu na wartość, która została podana jako ten argument, poza zakresem tej funkcji.
const kapitanowieEnterprise = { najlepszy_kapitan: 'Picard' }
function zastapKapitanow(kapitanowie) {
kapitanowie = { najlepszy_kapitan: 'Hook' }
}
zastapKapitanow(kapitanowieEnterprise)
kapitanowieEnterprise // => { najlepszy_kapitan: 'Picard' }Natomiast, jako, że obiekty nie są przekazywane do funkcji jako kopie, a jako wartości, to zmiana takiego obiektu w ciele funkcji wpływa na ten obiekt ogólnie - w zakresie, jak i poza zakresem, tej funkcji.
const kapitanowieEnterprise = { najlepszy_kapitan: 'Picard' }
function zmienKapitanow(kapitanowie) {
kapitanowie.najlepszy_kapitan = 'Janeway'
}
zmienKapitanow(kapitanowieEnterprise)
kapitanowieEnterprise // => { najlepszy_kapitan: 'Janeway' }Zwracanie wartości przez funkcję
Wywołana funkcja:
- zwraca wartość wyrażnie wskazaną, gdy zostanie użyte słowo kluczowe
return, a jej wywoływanie nie zostanie wstrzymane, - zwraca
undefined, gdy słowo kluczowereturnnie zostanie użyte, a jej wywoływanie nie zostanie wstrzymane, - zwraca
this, gdy zostanie wywołana przy użyciu słowa kluczowegonewjako konstruktor, a jej wywoływanie nie zostanie wstrzymane, - nic nie zwraca, gdy jej wywoływanie zostanie wstrzymane ze względu na błąd.
Zwrócenie wartości wyraźnie wskazanej poprzez użycie słowa kluczowego return:
function dodaj(a, b) {
return a + b
}
// Funkcja `dodaj` zwraca `4`, które następnie zostaje przypisane do zmiennej `suma`.
const suma = dodaj(2, 2)
suma // => 4Zwrócenie undefined poprzez pominięcie słowa kluczowego return:
function tylkoZaloguj() {
console.log('tylko loguje')
}
const foo = tylkoZaloguj() // tylko loguje
foo // => undefinedZwrócenie this poprzez użycie słowa kluczowego new:
function Osoba(imie) {
this.imie = imie
}
const wiedzmin = new Osoba('Geralt')
wiedzmin // => Osoba {imie: 'Geralt'}Nie zwrócenie niczego ze względu na wyrzucenie błędu podczas wykonywania funkcji:
function dodaj(a, b) {
return aa + b
}Funkcje jako obywatele pierwszej klasy (first-class citizens)
Funkcje w JavaScript są tak zwanymi obywatelami pierwszej klasy (first-class citizens), co oznacza, że mogą być:
- przekazywane jako argumenty do innych funkcji,
- zwracane jako wartości z innych funkcji, oraz
- przypisywane do zmiennych lub właściwości obiektów.
Przekazanie funkcji jako argumentu:
function wykonajMnie(funkcjaDoWykonania) {
funkcjaDoWykonania()
}
function zalogujFoo() {
console.log('foo')
}
function zalogujBar() {
console.log('bar')
}
wykonajMnie(zalogujFoo) // foo
wykonajMnie(zalogujBar) // barZwrócenie funkcji jako wartości z innej funkcji:
function utworzFunkcjeMnozaca(mnoznik) {
return (mnozna) => mnozna * mnoznik
}
utworzFunkcjeMnozaca(2)(21) // => 42Funckja może zostać przypisana do zmiennej lub właściwości obiektu:
const zalogujFoo = () => console.log('foo')
zalogujFoo()
const mapaLogerow = {
zalogujFoo: () => console.log('foo'),
zalogujBar: () => console.log('bar')
}
mapaLogerow.zalogujFoo() // foo
mapaLogerow.zalogujBar() // bar
const tablicaLogerow = [
() => console.log('foo'),
() => console.log('bar')
]
tablicaLogerow[0]() // foo
tablicaLogerow[1]() // barFunkcje wyższego rzędu /higher-order functions/
Gdy funkcja przyjmuje inną funkcję jako argument lub zwraca inną funkcję nazywana jest funkcją wyższego rzędu /higher-order function/. Pozostałe funkcje nazywane są funkcjami pierwszego rzędu /first-order functions/.
Przykładem funkcji wyższego rzędu jest funkcja map, której pierwszym parametrem jest funkcja.
const liczby = [1,2,3]
liczby.map(function(liczba) { return liczba * 2 }) // => [2,4,6]Podnoszenie /hoisting/
TODO
TODO
TODO
Function declarations are hoisted, initialized with the declared value and can be called in a given scope before their actual declaration. Compare it to hoisting of var, let, and const.
sum(2, 2) // => 4
function sum(a, b) { return a + b }Recursion
A function can call itself in three ways: 1) through calling its declared name, 2) through calling the reference to itself, and 3) through calling arguments.callee from within its body.
Closures
Functions in JavaScript are closures which mean they carry with themselves variables, constants and function declarations declared outside of their bodies but used within their bodies.