Funkcje

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 function z 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 declared

Wyraż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 function i 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 function wraz 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) // => 2

Wyraż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) // => 2

Wyraż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) // => 4

Wyraż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) // => 4

Nawiasy 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) // => 4

Parametryzacja 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) // => 6

Wpł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 kluczowe return nie zostanie użyte, a jej wywoływanie nie zostanie wstrzymane,

  • zwraca this, gdy zostanie wywołana przy użyciu słowa kluczowego new jako 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 // => 4

Zwrócenie undefined poprzez pominięcie słowa kluczowego return:

function tylkoZaloguj() {
  console.log('tylko loguje')
}

const foo = tylkoZaloguj() // tylko loguje

foo // => undefined

Zwró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) // bar

Zwrócenie funkcji jako wartości z innej funkcji:

function utworzFunkcjeMnozaca(mnoznik) {
  return (mnozna) => mnozna * mnoznik
}

utworzFunkcjeMnozaca(2)(21) // => 42

Funckja 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]() // bar

Funkcje 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.