TreningNr.1 Przyznaj się, na ile znasz JavaScript? Wiele programistów jest niedzielnymi programistami JavaScript. Coś trzeba było zrobić? Nie ma problemu. Metodą prób i błędów i przy pomocy Googli napiszę wystarczający działający kod JavaScript.

Nie ma w tym nic wstydliwego, że język JavaScript nie jest twoją główną smykałką. Dziś w roku 202X jednak trzeba coś z tym zrobić. 

Pomyśl też o tych sytuacjach, gdy szukasz prostych rozwiązania pod czysty JavaScript, a wyskakują Ci rozwiązania pod jQuery.

jQuery problem z JavaScript

Często też możesz mieć wrażenie, że Ty nie wybrałeś języku JavaScript. To on wybrał Ciebie.

Często też możesz mieć wrażenie, że Ty nie wybrałeś języku JavaScript. To on wybrał Ciebie.

Planuje zrobić webinar o "Szybki treningu JavaScript". Jego celem jest utrwalenie twojej wiedzy na temat JavaScript. Jak to zrobimy. Przypomnijmy sobie najważniejsze części języku JavaScript, jakim są nowinki z jego standardu ECMAScript 2015 zwanym również ES6.

Straszny JavaScript

Później pokaże Ci pewne techniki, które warto stosować w JavaScript w roku 2020.

Sprawdzimy także twoją wiedzę i zobaczymy, jak bardzo znasz JavaScript. Wpisy zrobimy w formie Quizu. Czyli pokaże Ci kod i zadać ci pytanie. To co startujemy :) 

 

Czy pamiętasz nową składnię z ES6: let i const

Słuchaj od 2015 roku kod JavaScript, piszę się zupełnie inaczej. Myślisz sobie "5 lat już minęło ,to każdy to już wie", ale jak to bywa z niedzielnymi programistami JavaScript, nawet jeśli coś ci się obiło o uszy. To wciąż nie jest to samo co usiąść na spokojnie z kodem i zrozumieć, dlaczego piszemy dziś taki kod. 

Sprawdźmy więc twoją wiedzę na temat "block scoping" czyli o zakresie zmiennych. 

console.log(personId);
var personId = 11;

Jak widzisz mamy zmienną personId, ale odwołujemy się do niej przed jej utworzeniem

Jak myślisz, co pojawi się konsoli JavaScript?

@@0001.png

Pojawi się undefined.

To jest przykład hoisting (windowania) w JavaScript.

Hoisting, spolszczona nazwa windowanie, to mechanizm pozwalający na przeniesienie deklaracji zmiennych oraz metod na początek funkcji.

Gdy deklarujemy zmienną personId przez słowo kluczowe VAR, to ulega ona windowaniu i na samej górze jest ona ustawiana jako undefined. Istnieje ona albo w obszarze danej funkcji, albo jak w tym przypadku globalnej przestrzeni, czyli global scope.

Co stanie, jeśli użyjemy słowa let

console.log(personId);
let personId = 12;

@@0002.png

Co otrzymamy w konsoli:

ReferenceError: personId is not defined

Gdy używamy słowa LET do deklaracji zmiennej, to wtedy Hoisting (windowanie) nie następuje.

To ma duży potencjał do rozwiązywania problemów i unikania błędów przez wielu programistów. Obecnie LET jest preferowaną deklaracją zmiennych, w końcu logiczne jest to, że powinieneś najpierw zadeklarować zmienną przed jej użyciem.

let personId = 13;
console.log(personId);

@@0003.png

Teraz wiesz, że taki kod da wynik: 13

Używając więc LET, upewniasz się, że użyjesz danej zmiennej dopiero po jej deklaracji. 

let personId
console.log(personId);

@@0004.png

Taki kod więc zwróci: undefined. Co jest logiczne. Takie zachowanie jest w każdym języku programowania.

Teraz spójrz na ten przykład. Deklarujemy PersonId i ponownie ją deklarujemy wewnątrz nawiasów klamrowych.

let personId = 15;
{
   let personId = 8000;
}
console.log(personId);

@@0005.png

Co pojawi się w konsoli: 15

Czyli mamy w JavaScript zakres zmiennych. Jak widzisz, możemy zadeklarować, ponownie zmienną w bloku. Gdy ten blok się zamknie, to wracamy do naszej zmiennej ze starą wartością.

{
   let personId = 9000;
}
console.log(personId);

@@0006.png

Co się stanie w konsoli więc gdy napiszemy taki kod?

ReferenceError: personId is not defined

Otrzymamy błąd. W końcu po skończeniu funkcji nasze lokalne zmienne też powinny być zlikwidowane. A co z tym przykładem?

function editPersonId() {
    personId = 16;
}
let personId = null;
editPersonId();
console.log(personId)

@@0007.png

Mamy funkcję, która ustawia personId i ustawia ją na 16, ale deklarujemy personId przed deklaracją funkcji. Co pojawi się w konsoli: 16

Deklaracja funkcji jest tymczasowo nieaktywna. Kompilator przejdzie przez to, mimo iż personId zostanie zadeklarowane później. Kod więc działa. A co z tym przykładem.

let id = 17;

for (let id = 0;id < 5;id++)
{

}
console.log(id);

Id jest deklarowane z wartością 17, a później jest ona używana jako zmienna do kontroli pętli.

__01.png

Jak myślisz jaka wartość będzie w konsoli.

Dostaniesz wartość 17.

Id w pętli jest przestrzeni pętli for i gdy ta pętla się skończy, ta zmienna też ulega destrukcji. 

Spójrz na ten kod 

let editFunctions = [];

for (var i = 5; i < 10; i++) {
   editFunctions.push(function() {return i;});
}

console.log(editFunctions[0]());

__02.png

Mamy pętle for, w której deklarujemy zmienną "i" słowem kluczowym VAR. W każdej iteracji ten pętli dodamy do tablicy nową funkcję, która ma zwrócić parametr "i". Parametr ten powinien być od 5 do 10 w zależności od indeksu w naszej tabeli funkcji.

Tak działają domknięcia czyż nie. 

Jak jednak jest? Co się stanie, gdy wywołam pierwszy element mojej tabeli funkcji.

Dostane wartość: 10.

Dostanę ciekawą liczbę. Myślałeś, że dostaniesz 5, ale tak nie jest. Mechanizm domknięcia jest tworzony dla zmiennej "i"  na końcu pętli for i na końcu pętli "i" ma wartość 10.

Pamiętaj, używamy tutaj słowa kluczowego VAR. Co się stanie, jak użyje słowa kluczowego LET.

let editFunctions = [];

for (let i = 5; i < 10; i++) {
   editFunctions.push(function() {return i;});
}

console.log(editFunctions[0]());

__03.png

Teraz mam... 5

Teraz mechanizm domknięcia będzie tworzony za każdy razem, gdy deklaracja naszej funkcji zostanie zakończona.  Co prawda silnik JavaScript musi się bardziej napocić, aby wykonać ten kod. Jednak jakbyś chciał, osiągnąć dokładnie taki cel to byś mógł to zrobić w taki sposób.

Stałe

Przejdźmy do stałych w JavaScript.

const PI = 3.14;
const ADDRESS = '10.0.0.1';
console.log(PI);
console.log(ADDRESS);

__04.png

Taki kod jak się domyślasz, zwróci te zmienne.

3.14

10.0.0.1

Jeżeli chodzi o konwencję nazewnictwa  stałych, to nie ma jasnego wymogu pisania ich wielką literą. Co się stanie, gdy spróbuje się odwołać do stały przed ich deklaracją?

"use strict"
const PI;
const ADDRESS;
console.log(PI);
console.log(ADDRESS);

__05.png

Otrzymamy:

Uncaught SyntaxError: Missing initializer in const declaration

Musimy więc zadeklarować stałe przed ich użyciem. A co się stanie, gdy spróbujemy nadpisać stałą.

"use strict"
const ADDRESS = 'localhost';
ADDRESS = '10.0.0.1'
console.log(ADDRESS);

__06.png

Otrzymamy:

Uncaught TypeError: Assignment to constant variable.

Nie możemy oczywiście zmieniać stałych.  Sama nazwa na to wskazuje. Co jednak się stanie, gdy napiszę taki kod?

"use strict";
const PI= 3.14;

if (PI > 0) {
    const PI = -3.14;
}

console.log(PI);

__07.png

W konsoli zobaczysz: 3.14

Czyli jak widzisz, stałe zachowują się jak zmienne deklarowane przez słowo LET. Jak jednak one radzą sobie ze złożonymi obiektami?

const myConstObject = {mutableProperty: 10};
myConstObject.mutableProperty = 20; 

console.log(myConstObject.mutableProperty); 

const myConstArray = [30];
myConstArray.push(40);

console.log(myConstArray); 

__08.png

Taki kod zwróci Ci:

20

[30,40]

Stałe więc pozwalają Ci zmieniać zawartość obiektów w nim znajdujących się, ale nie sam obiekt.

Arrow Functions: Funkcje strzałkowe

Funkcje strzałkowe pozwalają na stworzenie funkcji w pewnym specyficznym stylu. 

Tutaj mamy przykład stworzenia funkcji, która nie przyjmuje żadnych parametrów.

var getPhoneName = () => "9"
console.log(typeof getPhoneName);

__09.png

W konsoli wyświetli Ci się taki typ zmiennej. Czyli tak funkcja strzałkowa jest funkcją.

function

Czy można taką funkcję wywołać?

var getPhoneName = () => "9"
console.log(getPhoneName());

__10.png

Jak widzisz tak i to bez problemu. Otrzymasz 9.

Czy taki kod zadziała? Nie korzystam już z normalnych nawiasów? 

"use strict"
var getPhoneName = version => "Note " + version 
console.log(getPhoneName(10));

__11.png

Tak nie będzie problemu.

Note 10

Jednak jeśli masz więcej parametrów niż jeden, to nawiasy wracają.

var getPoints = (count,lifes) => count + (200 * lifes); 
console.log(getPoints(9000,2)); //9400

Jeśli chciałbyś, wykonać bardziej skomplikowaną logikę w swojej funkcji strzałkowej to dodajesz po strzałce nawiasy klamrowe.

var getPoints = (count,lifes) => {
   var lifeby  = lifes * 200;
   return count + lifeby;
}
console.log(getPoints(9000,2))

__12.png

Kod zwróci: 9400

Jaka jest rola funkcji strzałkowych. Czy chodzi o to, aby pisać mniej kodu? Czy istnieje jeszcze jakiś inny dodatkowy cel? Jak ten mechanizm działa ze słowem kluczowym this wewnątrz funkcji.

Słowo kluczowe THIS jest jedną z bardziej zakręconych spraw w JavaScript. Funkcje strzałkowe mają to ułatwić. Przynajmniej tak słyszałem :)

W tym przykładzie obsłużymy zdarzenie kliknięcia dokument.

document.addEventListener('click', function() {
   console.log(this);
});

__13.png

W konsoli powinieneś otrzymać: #document

W zależności od przeglądarki oznaczenie "dokumentu" może być różne, ale jak widzisz, dostaje definicje dokumentu w rezultacie. Słowo kluczowe "this" będzie się do tego odwoływać. 

Jak sprawa będzie wyglądać, gdy użyjemy funkcji strzałkowej.

document.addEventListener('click', () => console.log(this));

Czy zachowanie będzie takie same?

__14.png

Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}

Teraz dostajemy obiekt globalny Window. Nie jesteśmy, w obrębię kontekstu elementu funkcji jak wcześniej . W przypadku funkcji strzałkowych jesteśmy w kontekście kodu, który obecnie działamy, czyli w obiekcie Window. Tak długo, jak używamy funkcji strzałkowych, słowo kluczowe THIS nie będzie ustawiane jako obecny elementy, który dostaje zdarzenie.

Natomiast ustawi się tutaj obecny konteksty kodu, który wykonał to polecenie. Warto zaznaczyć, że będzie to element najwyższy w hierarchii elementów (rodziców i dzieci) i jest nim Window

To jedna z ważniejszych różnicy. Spójrzmy jednak na następny przykład z użyciem słowa kluczowego THIS.

Zobaczmy lepszy przykład.

Mamy tutaj obiekt wpisu na blogu ma on swój Id i funkcję odczytu. W tej funkcji wyświetlimy zawartość słowa kluczowego THIS. 

var post = {
    postId: 604,
    read: function () {
        console.log(this);
    }
};
post.read();

__15.png

Co pokaże się w konsoli?

{postId: 604, read: ƒ}

Dostajemy nasz obiekt z naszymi polami i funkcjami. THIS więc jest obecnym obiektem, na którym jest uruchomiona funkcja.

Teraz pytanie, co się stanie, gdy użyję funkcji strzałkowej. Jak widzisz, przykład jest prawie identyczny?

"use strict"
var post = {
    postId: 604,
    read: () => console.log(this)
};
post.read();

__16.png

Co pokaże się w konsoli?

Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}

Znowu ten obiekt Window. Znowu dostajemy kontekst, w którym uruchamiamy kod i jest to obiekt globalny window, który reprezentuje okno przeglądarki.

Czyli przy pomocy funkcji strzałkowej nie dostaniemy tutaj obiektu wpisu. Dostajemy kontekst, w którym uruchamiamy nasz kod.

Spójrzmy na kolejny przykład. Co, jeśli stworze funkcję, która zwróci funkcję strzałkową, która odniesie się do elementu THIS.

Co się dzieje ze słowem kluczowym THIS wewnątrz zwracanej funkcji? 

var post = {
    postId: 604,
    read: function () {
        return () => console.log(this.postId);
    }
};
post.read()();

__17.png

Co pokaże się konsoli?

604

Dostajemy 604, ale dlaczego? Kontekstem wykonywania kodu teraz jest obiekt wpisu, a więc mamy dostęp do pola postId.  

Spójrzmy na kolejne różnice w działaniach pomiędzy funkcją strzałkową a normalną funkcją.  

Wróćmy do normalnych funkcji. Co się stanie, gdy skorzystam funkcji call(), aby wywołać funkcję read dla nowego obiektu. Ten nowy obiekt ma inną wartość postId.

var post = {
    postId: 604,
    read: function () {
        console.log(this);
    }
};

post.read();
var newPost = {
    postId: 198,
};
post.read.call(newPost);

Dostanę oczywiście wartość: 198

Co się stanie, gdy będę chciał to samo zrobić z funkcją strzałkową?

var post = {
    postId: 604,
    read: function () {
        return () => console.log(this.postId);
    }
};

var newPost = {
    postId: 198,
};
post.read().call(newPost);

__18.png

Co pokaże konsola? 604

Czyli tak jakby cała funkcja Call() nie zadziała tak, jak by się tego spodziewał, bo otrzymałem starą wartość.

Co się stanie, gdy użyje funkcji bind() na metodzie strzałkowej.

var post = {
    postId: 604,
    read: function () {
        return () => console.log(this.postId);
    }
};

var newPost = {
    postId: 198,
};
post.read().bind(newPost)();

__19.png

Też będę miał starą wartość obiektu i w sumie mój nowy obiekt jest zignorowany. 

To jest bardzo ważne dla funkcji strzałkowych. Jak widać, nie możemy powiązać nowego obiektu, jak i go wywołać w funkcji strzałkowej. Można poczuć się zakłopotany, ponieważ silnik JavaScript nie wyrzucił błędu. Po prostu zignorował nowy obiekt i wykonał stary kod.

Pamiętaj, więc jeśli korzystasz z funkcji strzałkowych, nie będziesz miał możliwość zmiany powiązania funkcji z danym elementem. 

Funkcje call(), bind(), apply() są bezużyteczne dla funkcji strzałkowych. 

Przejdźmy więc do następnego przykładu:

var getScore = ()
      => 9000;

console.log(typeof getScore);

__20.png

Czy taki kod zadziała? W C# na pewno, ale jak to jest w JavaScript. 

Co pokaże się w konsoli?

Uncaught SyntaxError: Unexpected token '=>'

Dostaniemy błąd składni. W większość języków programowania nie wnika, w to ile znaków nowej linij w składni kodu ustawiłeś. W JavaScript wiele razy jednak znak nowej linii stanowi problem i funkcja strzałkowa jest jednym z nich. 

Czy funkcja strzałkowa ma właściwość prototype? Wszystkie obiekty w języku JavaScript wywodzą się od Object; wszystkie obiekty dziedziczą metody i pola po Object.prototype.

Normalnie funkcja ma taką właściwość i jest ona używana przy budowaniu funkcji. 

var getScore = () => 9000;
console.log(getScore.hasOwnProperty("prototype"));

__21.png

Jak myślisz, czy funkcja strzałkowa ma taką właściwość.

W konsoli jednak pokaże się wartość: false. Nie mamy dostępu do tej właściwości. 

Domyślne parametry w funkcjach

Spójrzmy na domyślne wartości funkcjach. Jeśli nie podasz parametru, do funkcji to automatycznie jest ona ustawiana na "undefined".  Masz oczywiście możliwość ustawienia domyślnych parametrów do funkcji.

Oto funkcja, która deklaruje w parametrach domyślne wartości.

var getGame = function(id = 0,type="none") {

    console.log(id + ", " + type);
}

getGame(undefined, "RPG");

__22.png

Co się pojawi w konsoli?

0,RPG

Teraz czy możemy wykonywać działania arytmetyczne wewnątrz deklaracji domyślnej funkcji?

Chciałbym mieć możliwość dodania punktów, w zależności od tego ile punktów życia mi zostało. Alternatywnie chciałbym sam podać wartość dodatkowych punktów.

var getTotalScore = function(score = 0,lives = 0, 
addscore = lives * 200) {

    console.log(score + addscore);

};
getTotalScore(5000,2);

_23.png

Konsola mówi:

5400

Tak można robić i jak widzisz, punkty zostały odpowiednio sumowane.

Czy domyślne wartości mogą być określane przez funkcję w swojej składni?

var generatePoints = () => 200;
var getTotalScore = function(score = 0,lives = 0, 
addscore = lives *  generatePoints()) {

    console.log(score + addscore);

};
getTotalScore(5000,2);

_24.png

Odpowiedź brzmi tak:

5400

Jak na razie nic dziwnego się nie dzieje z domyślnymi wartościami.

Często przed ES6 programiści JavaScript lubili sprawdzać ile parametrów ma funkcja. Jak myślisz, jak ten kod zadziała. Czy dostaniemy liczbę 3, bo w końcu tyle argumentów może przyjąć maksymalnie moja funkcja? Czy może dostaniemy 2, bo tyle argumentów wysyłamy do funkcji?

var generatePoints = () => 200;
var getTotalScore = function(score = 0,lives = 0, 
addscore = lives *  generatePoints ()) {

    console.log(arguments.length);

};
getTotalScore(5000,2);

_25.png

Konsola prawdę Ci powie.

Otrzymamy 2, czyli tyle ile argumentów podaliśmy. Warto więc wiedzieć, do czego pole arguments się referuje.  

Teraz mamy sytuacje, w której używamy domyślnego parametru dwa razy. Nie przekazujemy niczego do funkcji getName(), a więc powinny być wykonane wartości domyślne.

var getName = function(name = defaultname, defaultname = "MR"){
    console.log(defaultname + " " + name);
}

getName();

_26.png

Co pojawi się w konsoli?

Uncaught ReferenceError: Cannot access 'defaultname' before initialization

Dostaniemy błąd, gdyż próbujemy odwołać do parametru defaultname, zanim on zostanie utworzony. JavaScript nie patrzy w przyszłość kodu, jak widzisz.

A co się stanie, jak podam parametr do funkcji?

var getName = function(name = defaultname, defaultname = "MR"){
    console.log(defaultname + " " + name);
}

getName("Cezary");

_27.png

Jak widzisz?

MR Cezary

Wszystko działa. Czy możemy utworzyć dynamiczną funkcję z parametrami domyślnymi?

var getName = new Function("name = 'Cezary'","return name");
console.log(getName());

_28.png

Tak wszystko zadziała

Składnia rozwinięcia: Rest i Spread

Rest określa technikę zbierania parametrów i umieszczania ich do pojedynczej tablicy. Spread natomiast określa technikę rozbijania elementów tablicy czy nawet napisu.

Zobaczmy na przykłady użycia.  Mamy funkcję "showTags" i jak widzisz drugi parametr funkcji, ma trzy kropki na początku.

Te trzy kropki są tutaj symbolem operacji "Rest". Zbiera ona wszystkie dane podane przez nasze parametry i umieszcza je w tablicy.

var showTags = function (postId, ...tags){
    console.log(tags instanceof Array);
};

showTags(777,"Java","Wzorce-Projektowe","C#");

_29.png

Czy parametr tags jest tablicą? Tak w konsoli otrzymasz wartość "true".

var showTags = function (postId, ...tags){
    console.log(tags);
};

showTags(777,"Java","Wzorce-Projektowe","C#");

_30.png

Prawie podobnym kodem możesz wyświetlić zawartość takiej tablicy.

["Java", "Wzorce-Projektowe", "C#"]

Co się stanie jak, nie podamy parametrów w funkcji?

var showTags = function (postId, ...tags){
    console.log(tags);
};

showTags(777);

_31.png

W konsoli otrzymamy? Pustą tablicę [].

Pustą tablicę, chociaż mógłbyś się spodziewać wartości undefined. W końcu w JavaScript nie wszystko jest takie oczywiste.

Jak jest parametrem length w funkcji, która powinna określić liczbę parametrów.

var showTags = function (postId, ...tags){};
console.log(showTags.length);

_32.png

Co pojawi się w konsoli? 

1

Mógłbyś myśleć, że w teorii parametrów może być nieskończenie wiele, albo dziwną logiką dwa. Jest jednak fizycznie 1 parametr w tej funkcji według silnika JavaScript.

Jaką jednak wartość otrzymam, gdy będę chciał sprawdzić ilość argumentów wewnątrz funkcji.

var showTags = function (postId, ...tags){

    console.log(arguments.length);

};
showTags(777,"Java","Wzorce-Projektowe","C#");

_33.png

W takim wypadku otrzymam: 4

Jak widzisz arguments, ponownie odwołuje się do ilości argumentów, które przesłaliśmy do funkcji.

Czy są jakieś problemy z utworzeniem dynamicznej funkcji?

var showTags = new Function("...tags", "return tags;");
console.log(showTags("Java","Wzorce-Projektowe","C#"));

_34.png

Nie. Otrzymamy w takim przypadku tablice.

To by było na tyle, jeśli chodzi o technikę rest, teraz zobaczmy, jak działa technika spread. Czyli teraz będziemy zamieniać tablice na...zaraz zobaczysz.

Na początek zobaczmy, jak działa funkcja "max." z obiektu Math. Jak widzisz, trzy kropki teraz są w definicji użycia funkcji. To jest operacja spread w tym wypadku.

var values = [11,12,33,44,13,45];
var maxValue = Math.max(...values);
console.log(maxValue);

_35.png

Dostaniemy wartość 45. Co się stało w tym momencie? Operacja "spread" zmienił tablicę na listę parametrów.

Co więc powinien zrobić taki kod? Czy utworzymy w ten sposób nową tablicę?

var values = [11,12,33,44,13,45];
var valuesArray = [...values];
console.log(valuesArray );

_36.png

Tak w taki sposób dostaniemy nową tablicę. A co się stanie w takim przypadku? Nie mamy żadnych ciekawych wartości w tablicy? Co się stanie? Gdzieś w silniku JavaScript ta składnia będzie musiała wczytać dane, których nie ma.

var values = [,,,,];
var valuesArray = [...values];
console.log(valuesArray );

_37.png

Co otrzymamy w konsoli?

[undefined, undefined, undefined, undefined]

Otrzymujemy tablice z 4 elementami undefined. Dodatkowo JavaScript uznaje, że nie ma elementu po ostatnim przecinku. 

var values = [1,2,3,4,];
var valuesArray = [...values];
console.log(valuesArray );

_38.png

Taki kod da: [1,2,3,4] , a nie [1,2,3,4,undefined]. W taki sposób możesz tworzyć nowe tablice

var valuesArray = [...[1,2,3,4,]];
console.log(valuesArray );

Jak myślisz. Co zwróci taki kod?

var maxNumber = Math.max(..."987654321");
console.log(maxNumber);

_39.png

Otrzymasz wartość "9". Dlaczego? Operator spread potraktował ten napis jako tablice znaków i rozbije ją na listę takich parametrów. Później te parametry zostały zmienione na cyfry.

Co pojawi się w konsoli, gdy utwórzmy tablice z pierwszym elementem "CEZ", a później użyjemy spread operatora na słowie "WAL". Na koniec dodamy parametr "PO SCIANIE".

var wordsArray = ["CEZ",..."WAL","PO SCIANIE"];
console.log(wordsArray);

_40.png

Otrzymamy tablice:

["CEZ", "W", "A", "L", "PO SCIANIE"]

Definiowanie obiektów

Wiesz, że można w JavaScript definiować obiekty na ciekawe sposoby. Jak myślisz, czy taka składnia jest poprawna?

var price = 7.99, tax = 0.2;

var product = {
    price,
    tax,
    "calulacte priceWithTax"() {
        return this.price * this.tax;
    }
}; 

console.log(product["calulacte priceWithTax"]());

_41.png

Odpowiedź brzmi tak i ta składnia istnieje po to, aby deklarować właściwości ze spacją. Konsola więc napiszę, że mamy taką wartość.

1.598

Czy w JavaScript można dynamicznie nazywać właściwości? Zobaczmy czy taka składnia przejdzie.

var field = 'dynamicPrice';
var field2 = 'dynamicTax'
var price = 7.99, tax = 0.2;
var product = {
    [field] : price,
    [field2 ] : tax ,
}; 

console.log(product);

Tak jest to możliwe.

_42.png

{dynamicPrice: 7.99, dynamicTax: 0.2}

Nie ma też problemu z wyrażeniami wewnątrz tych dynamicznych właściwości.

var field = 'dynamicPrice';
var field2 = 'dynamicTax'
var price = 7.99, tax = 0.2;
var product = {
    [field + "-1"] : price,
    [field2 + "-1"] : tax ,
}; 

console.log(product);

_43.png

Pętla for...of

Pętla for...of to ciekawa nowość z ES6. Logiczne jest to, że pozwala ona iterować po tablicach. Czy można też po napisach?

var codes = "CZEF";
for (var code of codes)
{
    console.log(code);
}

_44.png

Tak można i konsola wyświetli: C,Z,E,F. Jak myślisz, można iterować, po obiektach. Jeśli tak to, co taki kod by wyświetlił.

var post = {id:11,title:"m",content:"l"};

for (var p of post)
{
    console.log(p);
}

_45.png

Nie wyświetli się nic, bo nie można iterować w takiej pętli po obiektach. Jeśli myślałeś inaczej, to pomyliłeś pętle for...of z pętlą for...in

var obj = {
    name: "Simon",
    age: "20",
}

for(var propt in obj){
    console.log(propt + ': ' + obj[propt]);
}

_46.png

Szablony: Template Literal

Każdy język programowania powinien mieć łatwy sposób na wyświetlanie zmiennych w poszczególnych miejscach w napisach. JavaScript też ma taką możliwość tylko jak jej użyć. Spójrz na ten kod. 

let numer = 888;
console.log('Numer : ${numer}');
let numer2 = '888';
console.log('Numer : ${numer2}');

let numer3 = 999;
console.log(`Numer : ${numer3}`);

_47.png

Jak myślisz, które wyrażenie jest poprawne?

Numer : ${numer}
Numer : ${numer2}
Numer : 999

Ostatnie wyrażenie jest poprawne, bo aby literał ${} zadział. Trzeba stworzyć najpierw napis znakiem `. Znakiem Grawisu https://pl.wikipedia.org/wiki/Grawis

Na klawiaturze znajduje się ona trochę wyżej od tabulatora.

Gdybyś chciał uniknąć użycia szablonu, to najpierw musisz użyć znaku backslash "\"

let numer4 = 777;
console.log(`Numer : \${numer3}`);

Spójrz na ten kod? Myślisz, że znak Grawisu coś tu pomoże. Przecież wiadomo, że JavaScript nie lubi znaków nowej linii. 

let what = `K
O
C
H
A JAVASCRIPT`;
console.log(what);

_48.png

Okazuje się jednak, ze grawis przyjmie znaki nowej linii i wyświetli właśnie tak napis w konsoli.

Jak ten literał jest realizowany i kiedy? To jest dobre pytanie. Spójrz na ten kod. Jak myślisz, ile trzeba kupić jabłek 3 czy 20.

function showCommand(command) {
    let ile = 3;
    console.log(command);
}
let ile = 20;
showCommand(`Kup ${ile} jabłek`);

_49.png

Interpolacja zachodzi, zanim funkcja zostanie wywołana więc mamy kupić 20 jabłek. Warto o tym pamiętać.

Teraz pora pokazać Ci coś, co nazywa Template Tag Literal. Wygląda jak wywołanie funkcji tylko bez nawiasów. Co ciekawe to wyrażenie wykona funkcje process.  Przeniesiemy do tej funkcji literał jako parametr.

function process(text) {
    console.log(text);
}

process `przyklad`;

Co dostaniesz, gdy wykonasz ten kod:

Dostaniesz tablice ["przyklad"]. Ta tablica reprezentuje jedną wartość tego literału i nazywa się segmentem. Nadal ciężko jednak zobaczyć, o co chodzi. Zobaczmy bardziej zaawansowany przykład.

Mam dwie zmienne i wykonam teraz funkcje process, wysyłając literał z dwoma parametrami. 

Funkcja process przyjmuje dwa parametry, jak widzisz. Jedną z nich jest segment, a drugą będą napisy przesłane w parametrach w literale. Wiemy, że będą one tablicą. 

function process(segments,...textes) {
    console.log(segments);
    console.log(textes);
}

let headerText = "Blog Stefana";
let title = "Co nowego ES2030";
process `-> ${headerText} wpisy ${title} <-`;

Co pojawi się w konsoli? 

(3) ["-> ", " wpisy ", " <-"]
(2) ["Blog Stefana", "Co nowego ES2030"]

W segmencie dostaniesz tablice elementów napisu, które były pomiędzy naszymi zmiennymi. Czyli strzałki oraz słowo "wpisy". 

W parametrze "textes" natomiast mamy tablicę napisów naszych literałów.

Tak możesz stworzyć funkcję, która przetworzy według twojej woli twój napis.

Dekonstrukcja

Dekonstrukcja polega na przekształcaniu pewnych struktury zazwyczaj tablic. Przykładowo taki kod przekształci poszczególne wartości tablicy na konkretne zmienne.

let programmersSalary = [3000,5000,7200,8500,10000,15000];
let [lowest,low,avarage,ok,yes,hight] = programmersSalary;
console.log(lowest);
console.log(avarage);

_50.png

W konsoli wyświetli się wartości: 3000 i 7200.

Czy możesz ignorować pewne elementy w tablicy, gdy robisz dekonstrukcję? Odpowiedź brzmi tak.

let programmersSalary = [3000,5000,7200,8500,10000,15000];
let [lowest,,,,,hight] = programmersSalary;
console.log(hight);

_51.png

Czy możesz pozostałe wartości przekształcić w nową tablicę? Odpowiedź brzmi tak.

let programmersSalary = [3000,5000,7200,8500,10000,15000];
let [lowest,low,...remaining] = programmersSalary;
console.log(remaining);

Gdyby tablica byłaby zagnieżdżona to czy nadal mógłbym zrobić dekonstrukcje. Odpowiedź brzmi tak.

let programmersSalary = [3000,5000,7200,[8500,10000,15000]];
let [lowest,low,avarage,[seniorlowest,senioravarage, seniorHigh]] =
 programmersSalary;
console.log(seniorlowest);

_52.png

Czy obiekty też można dekonstruować? Odpowiedź brzmi tak.

let game = {
    title: 'Mortal Kombat 2',
    desc: 'bla bla',
    platform: 'Amiga'
};

let {title,desc} = game;
console.log(title);

Co więcej, można nadać zmiennym inną nazwę niż ma przypisana  właściwość

let game = {
    title: 'Mortal Kombat 2',
    desc: 'bla bla',
    platform: 'Amiga'
};

let {title:main,desc:whatabout} = game;
console.log(whatabout);

Odpowiedź brzmi tak.

A czy można rozbić undefined.

let [title,desc] = undefined;
console.log(`title ${title} desc ${desc}`);

_53.png

Okazuję się, że można dekonstruować tylko rzeczy, które mają obsługę iteracji. O iteratorach pomówimy, kiedy indziej. 

Uncaught TypeError: undefined is not iterable

Analogiczny błąd pojawi się dla wartości null.

let [title,desc] = null;
console.log(`title ${title} desc ${desc}`);

_54.png

Uncaught TypeError: null is not iterable

Jak myślisz, czy taki kod zadziała. 

for (let [a,b,c] of [[10,20,30]]) {
    console.log(`${a} ${b} ${c}`);
}

Tak. W taki sposób można wyciągnąć elementy, które są w tablicy tablic.

Podsumowanie

Spojrzeliśmy na podstawy składni ES6, która istnieje od 2015 roku. Mam nadzieje, że ten wpis zweryfikował twoją wiedzę.

W następnym wpisie zobaczymy jak moduły i klasy w JavaScript działają. Chociaż może najpierw omówimy tablice i kolekcje w JavaScript.