User Tools

Site Tools


knowledge_base

JavaScript, ES6 knowledge base

JavaScript

Funkce

Deklarace funkce

  • Takováto funkce je načtena ihned, jakmile se spustí program. Drží se v paměti a čeká se na to, až ji použijeme.
function sectiCisla(a, b) {
	return a + b;
}

Výraz funkce (function expression deklarace)

  • Funkce je uložena do proměnné a je načtena do paměti teprve tehdy, až se na ni narazí v programu.
var scitatko = function sectiCisla(a, b) {
	return a + b;
};
scitatko(5, 10);

Lze z toho vyrobit i anonymní funkci:

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

Výhodou tohoto je, že funkce mohou být uloženy do proměnné a vloženy jako parametry do další funkce, kde jsou posléze použity.

Funkce může vracet funkce

  • Takto lze dělat Immediately invoked function
 function vratListek(a, b, c) {
    return function() {
      alert("neco");
    }
 }
 vratListek(a, b, c)();
 

Díky té závorce na konci se ta uvnitř vložená funkce vyvolá.

Closures

  • Pokud ve funkci vracíme funkci, viz výše, a je to kompletně s proměnnými, které se ukázaly v externí funkci.
  • V JavaScriptu mají všechny funkce přístup ke scope “nad nimi”.
  • Vnořené funkce mají tedy přístup ke scope “nad nimi”.
 function testClosure() {
var x = 4;
function closeX() {
	return x; -> tohle je closure
}
return closeX;
 }   

Vyvolání:

 var closedX = testClosure();
 closeX(); // 4
 

A ještě jeden příklad:

 var add = (function () {
     var counter = 0;
     return function () {
        return counter += 1;
     }
 })();
 add();
 add();
 add();

Hodnota counter bude 3.

  • proměnné add je přiřazena hodnota self-invoking funkce
  • ta self-invoking funkce běží pouze jednou - nastaví counter na hodnotu 0 a vrátí výraz funkce
  • proměnná counter je chráněna scopou anonymní funkce, může být změněna jen vyvoláním funkce add.

Metody call() a apply()

  • obě metody ihned vyvolávají funkce, jakmile jsou použity.
call()
  • Pokud používáme tyto metody, můžeme změnit kontext, když vyvoláváme funkci.
let princess = {  
   name: 'Leia Organa',
   say: function(msg) { console.log(this.name + ": " + msg); }
};
princess.say("I love you!");  
// => Leia Organa: I love you!

V případě výše je this odkazováno přímo na objekt princess.

Pokud použijeme call(), tak prvním parametrem říkáme, který kontext máme pro vyvolání funkce použít.

let name = 'Han Solo';  
princess.say.call(this, "I know.");  
// => Han Solo: I know.

Takže jsme přepsali vlastnost name.

apply()
  • rozdílem oproti metodě call() je to, že do apply() vstupují pouze 2 parametry - kontextový objekt (stejně jako u call) a dále pole argumentů (místo abychom tam posílali parametry jeden za druhým).
  • i když do apply vstupují argumenty jako pole, do funkce vstupují normálně.
function add(a,b) { console.log(a + b); }  
add.apply(null, [2,3]);  
// 5

V příkladu výše se použil jako kontextový objekt hodnota null. Dle ECMAScript standardů způsobí null nebo undefined vstupující do volání to, že lexikální scope (to je to, že každá vnitřní úroveň má přístup ke své nadřazené úrovni) funkce se změní na globální.

Why would we use .apply() and not just use .call() everywhere? Let's say you had a function called after() that would wrap an existing function and ensure some code was executed every time after that function was called. Using .call() would be nearly impossible given that you don't know the number of parameters that function might be called with - without resorting to something potentially dangerous like using eval().

This is where .apply() and the arguments local variable come to the rescue:

function after(fn) {  
  var orig = fn;
  return function() {
    var args = [].slice.call(arguments);
    return orig.apply(null, args);
  };
}

Vyvolání:

var lots = after(lots);  
lots(1,2,3,4);  
/// 1,2,3,4

Bind()

  • Můžeme bindovat jak kontext tak více parametrů a ihned funkci nevyvolávat. Můžeme si takto funkci nabindovat a zavolat ji později.
var doctor = {  
  name: 'Matt Smith',
  who: function named() {
   console.log(this.name);
  }
};
function after(fn, context) {  
  var orig = context ? fn.bind(context) : fn;
  return function() {
    var args = [].slice.call(arguments);
    return orig.apply(null, args);
  };
}
var tenth = { name: 'David Tennant' };  
var thedoctor = after(doctor.who, tenth);  
doctor.who();  // Matt Smith  
thedoctor();   // David Tennant  

.bind() also allows us to pre-bind one or more argument parameters to the function as well. For instance:

function add(a,b) { return a + b; }  
var add2 = add.bind(null, 2);  
add2(4);   // 6  
add2(3,6); // 5  

Here, we create a new function by partially applying the first argument to add(). Passing any subsequent parameters makes no difference.

Keep in mind that .call(), .apply() and .bind() can not be used with ES6's ⇒ functions to change the context of this, as this is explicitly bound to the enclosing scope where the function is declared. You can use .apply and .call to pass in argument parameters, but the first argument is ignored for changing context.

Pole (Array)

  • Pokud použijeme pojmenovaný index (viz níže), JavaScript předefinuje pole na standardní objekt.
 var person = [];
 person["firstName"] = "John";
 person["lastName"] = "Doe";
 person["age"] = 46;
 var x = person.length;         // person.length will return 0
 var y = person[0];             // person[0] will return undefined
 var z = person.firstName;             // person.firstname will return John :))

Kontrola, zda je něco pole:

  • Array.isArray(fruits)
  • nebo instanceof Array

Metody pole ES5

  • toString()
  • join(“separator”)
  • pop() - odstraní poslední prvek z pole
  • push(“neco”) - přidá prvek na konec pole
  • shift() - Odstraní první prvek z pole, neb to pole posune doprava. Upraví i velikost pole, zároveň návratovou hodnotu toho shift je prvek, co byl odstraňován
  • unshift() - Přidá prvek na začátek pole, posune to a rozšíří pole - návratová hodnota není ten prvek ale nová velikost pole
  • splice(pozice, kolik_prvku_se_ma_odstranit, “novy prvek”)
  • concat(pole_ktere se ma pripojit k poli, na kterem je zavolana metoda concat)
  • slice(cislo_prvku_od_kteryho_ma_byt_vytvoreno_nove_pole) - rozděluje pole, ale původní zůstává zachováno. Zároveň vzniká pole nové.
  • sort()
  • revert()
  • sort(function(a, b) { return a - b}) - třeba komparátor

Objekty v JavaScriptu

Téměř všechno v JavaScriptu jsou objekty:

  • Booleans can be objects (if defined with the new keyword)
  • Numbers can be objects (if defined with the new keyword)
  • Strings can be objects (if defined with the new keyword)
  • Dates are always objects
  • Maths are always objects
  • Regular expressions are always objects
  • Arrays are always objects
  • Functions are always objects
  • Objects are always objects

Nejběžnější způsob vytvoření je přes tzv. objektový literál.

  • var myBox = { }; - chlupaté závorky říkají “vytvoř nový objekt” (s žádnými properties)
  • takto lze vytvořit objekt naplněný: var myBox = { height: 8, width: 6, material: “cardboard”, contents: [“neco”, “dalsivec”] };
  • pokud objektu přiřadíme vlastnost přes vnější proměnnou, tak se modifikuje reference, není tam nic jako kopie
  • k vlastnostem objektu lze přistupovat buď přes DOT operator (přes tečku) nebo přes BRACKET operator (přes závorky hranaté).
  • Z objektu mažeme pomocí slova delete - maže se nejen hodnota, ale i property, která je přiřazena. Např. delete myBox.height

Objektová funkcionalita

  • Uvnitř objektu samozřejmě lze deklarovat funkce:
addWorker: function(..., ...) {
         ...
}

Lze definovat i zvenčí:

 ourObject.removeWorker = function(.., ..) {
 	...
 }
  

Prototyp

  • Všechny objekty mají předka - “Object prototype”
    • mají vlastnosti:
      • valueOf
      • constructor
      • toLocaleString
      • toString
      • isPrototypeOf
      • propertyIsEnumerable
      • hasOwnProperty
  • Pole například také dědí - od “Array Prototype”
    • pop()
    • push()
    • length
    • sort()
    • slice()
    • atd.
  • Stringy mají “String prototype”
    • concat()
    • charAt()
    • atd.
  • To samé čísla
  • Nebo funkce
    • myFction.apply()
    • myFction.call()
    • myFction.bind()

Pokud bychom zatoužili přidat všem řetězcům nějakou funkci, uděláme to:

 String.prototype.countAll = function(letter) {
 	...	
 };
 

Konstruktory a dědičnost

Objekt lze vytvářet podle již existujícího objektu pomocí Object.create

 var shoe = { size: 6, type: "woman" };
 var magicShoe = Object.create( shoe );
 

Ten nově vytvářený objekt zdědí všechny vlastnosti toho, ze kterého byl vytvořen.

Hoisting

  • TLDR; Vždy deklarovat všechny proměnné nahoře.
  • jde o koncept týkající se pořadí načítání proměnných a funkcí do paměti
  • defaultní chování přesouvání deklarací nahoru v rámci aktuální scope (navrch aktuálního skriptu nebo funkce)
    • proměnná v JavaScriptu může být deklarována až poté, co je někde výše použita
    • inicializace nejsou hoistovány (hoistují se pouze deklarace, ne inicializace)

Proměnné

 function func() {
    console.log(a);
    var a = 1;
 }

Vypíše undefined

 function func1() {
    a = 1;
    console.log(a);
    var a;
 }

Vypíše 1

Funkce

  • Při volání inc() je to ok, funkce byla hoistována.
  • Proměnná a bude ale undefined.
  • Funkce dec() bude také undefined, neboť pro to platí stejná pravidla jako pro přiřazenou proměnnou do deklarace (dec() je funkce přiřazená do deklarace a není hoistována)
 (function() {
    console.log(inc(4));  // 5
    console.log("a =",a); // a = undefined
    console.log(dec(4)); // ReferenceError: dec undefined
    var a = 1;
    var dec = function(n) { return --n; }
    function inc(n){ return ++n; }
 })();
  • Když to nyní změníme na zápis níže, tak už bude fungovat i výpis proměnné a
 (function(){
     a = 4;
     function inc(n){ return ++n; }
     console.log(inc(4));   // 5
     console.log("a = ", a); // a = 5
     var a; // Ah-ha!
 })(); 
 

Řešením proti nežádoucímu chování hoistingu může být používání ES6 klíčových slov let a const pro deklaraci proměnných a funkcí.

Zajímavé je, že když hoistujeme s klíčovým slovem let, tak nedostáváme chybu undefined ale ReferenceError:

console.log("a = ", a);  
console.log("b = ", b);  
var a = 4;  
let b = 6;  
// => a = undefined
// => b = ReferenceError!

Scopes

  • Scope je v JavaScriptu množina proměnných, objektů a funkcí, ke které máme přístup.
  • Proměnné uvnitř funkcí mají lokální scope, globální proměnné mají globální scope a mohou k nim přistupovat všechny skripty a funkce v kódu!
  • Globální proměnná vzniká tak, že ji neuvedeme klíčovým slovem var - ty globální bychom vůbec neměli tvořit.
  • Pokud používáme “Strict Mode”, tak globální proměnné nebudou fungovat.
  • Lokální proměnné jsou mazány z paměti jakmile je funkce dokončena.
  • Více o scopes zde: JS scopes
  • deklarace tříd nejsou hoistovány!

Příklady

 var a = 4;
 function foo(x) {
 	var b = a * 4;
function bar(y) {
	var c = y * b;
	return c;
}
return bar(b);
 }  	

V příkladu výše jsou 3 oddělené scopes:

  1. defaultní globální, kde je “a” a funkce “foo”
  2. další scope deklarovaná uvnitř foo, což je “x”, “b” a “bar”
  3. scope uvnitř bar, což je “y” a “c”

Vnořené scopes mohou přistupovat k proměnným deklarovaným v jejich rodičovských scopes (tzv. scope lookup chain)

To umožňuje mimojiné například používat closures.

var times = function(n) {  
    return function(x) {
        return x * n;
    };
}
var times2 = times(2);  
console.log(times2(4));  
// => 8

Zanořené scopes mohou překrývat proměnné deklarované v jejich rodičovské scope.

var a = 4;  
function foo(x) {  
   var a = x;  // shadows parent 'a' declaration
   console.log(a);
}
console.log(foo(6));   // => 6  
console.log(a);        // => 4  

Best practises

Ternární operátor

  • podminka ? “kdyz ok tak moznost1” : “kdyz nesplnena tak moznost2”

Přiřazování pomocí logických operátorů

 var neco = necoJinyho || [ ]
  • Do něco je přiřazena první hodnota, která není nějak chybová (undefined, null, 0, “”, …)
  • Pokud nic není splněno, přiřadí se poslední chybová hodnota
 var lost;
 lost = 4 || undefined;

Pro logický součet platí následující pravidla:

  • Když levý v AND je false, vrať do proměnné ten levý (tzv. short-circuit)
  • Když jsou oba výrazy pravda (var neco = “neco” && “necojinyho”;), tak bude přiřazena hodnota “necojinyho” - AND se vyhodnocuje pak zprava.
  • Když všechny výrazy nepravdivé, tak se vrací opět ta první hodnota.

Switch

JavaScript může ve switch příkazu míchat datové typy.

Eval

  • funkce eval() vyhodnocuje nebo vykonává to, co do funkce vstupuje jako argument (reprezentováno jako string)
  • argumentem může být proměnná, výraz (ten je poté vyhodnocen), může jím být také jeden nebo více JavaScriptových příkazů. Funkce eval() v takovém případě vyhodnocuje příkazy.
  • jedná se o poměrně nebezpečnou funkci, neboť dokáže vykonávat kód, který do něj vloží ten, co tu funkci volá. Může to být například zneužito, když by nám nějaký útočník podsunul svůj string.

ES6

TODO

Let a Const

  • let a const mají tzv. block-scope oproti var, které má function-scope
  • Je to šikovné v okamžiku, kdy si nechceme jinou scope zasvinit více proměnnými než potřebujeme.
 function func(){
     let a = 1;
     if (true) {
         let b = 2;
     }
     return a + b; // Uncaught ReferenceError: b is not defined
 }
 
 

Proměnná b má v příkladě výše tzv. block-scope, takže její existence je vázána k bloku s podmínkou, není tedy dostupná v rámci celé té funkce.

Co se týče const, tak to je stejné jako let, akorát je immutable. Pokud do proměnné deklarované jako const jednou přiřadíme proměnnou, už ji nemůžeme změnit (dostaneme chybu TypeError).

věc k zapamatování: immutable je pouze ta proměnná, nikoliv její hodnota, takže pokud máme v proměnné přiřazen objekt, můžeme mu libovolně přidávat či měnit properties.

Lexikální scope

  • V ES5, když jsme chtěli mít okamžitě vyvolanou funkci, definovali jsme to takto:
 (function() {
    var a = 1;
    console.log(a);
 })();
 

V ES6 to můžeme zapsat takto:

  {
    let a  = 1;
    console.log(a);
  }

Nebo:

var a = 1, b = 2, c = 3;  
{
  let a = 4, b = 5, c = 6;
  console.log("a,b,c = ", a, b, c);
}
console.log("a,b,c = ", a, b , c);  
// => a,b,c = 4 5 6
// => a,b,c = 1 2 3

This

  • klíčové slovo this
  • na co vlastně odkazuje this použité uvnitř funkce?
    • nejznámější použití je ve funkcích, které se používají jako konstruktor s klíčovým slovem new

Když se použije new, odkazuje se na objekt, který je navrácen z funkce

function Tribble(color) {  
  this.color = color;
}
var my_tribble = new Tribble("brown");  
my_tribble.color;  // "brown"  

Když se volá metoda na objektu, odkazuje se na ten objekt, v jehož kontextu je to voláno

var tardis = {  
  where: "Gallifrey",
  go: function() { 
    console.log("Off to " + this.where + ", Allons-y!"); 
  }
};
tardis.go();  // "Off to Gallifrey, Allons-y!"  

Ale pozor, i bez kontextového objektu se po zavolání this stále odkazuje na kontext, ve kterém je funkce - window pro prohlížeče

      var where = "Gallifrey";  
function go() {  
  console.log("Off to " + this.where + ", Allons-y!"); 
}
go();  // "Off to Gallifrey, Allons-y!"  

Arrow funkce =>

  • Zjednodušuje se zápis. Místo function func() { … } můžeme nyní psát func() { … } a díky tomu získáváme výhodu inline zápisu:
 [1, 2, 3].map(function(el) {
     return el + 2;
 });
 

Lze zapsat jako:

 [1, 2, 3].map(el => el + 2); // you can omit return and ()
 

Pokud máme více než jeden argument, lze využít:

 let a = [1, 2, 3].reduce((acc, current) => acc + current);
 console.log(a); // 6   
 

Toto vlastně udělá to, že jako první argument si vezme položku z pole, current je aktuální hodnota budoucího výstupu, takže se iteruje nad polem postupně jako:

  • 1 + 0 ( = 1)
  • 2 + 1 ( = 3)
  • 3 + 3 ( = 6)

Pokud máme více než jeden řádek, tělo funkce za ⇒ lze obalit do chlupatých závorek ⇒ { … }

Samozřejmě lze tento zápis funkce přiřadit i do proměnné.

Náhrada za var self = this

  • Skvělá věc na arrow funkcí je, že se vyhneme zastaralému používání var self = this.
  • Například chci v rámci funkce sendEmail odeslat email, když je stisknuto tlačítko. Mail chci odeslat na email, který je v rámci instance třídy Contract. Pokud bych použil klíčové slovo this, kontextem je v tu chvíli tlačítko. Kdysi se to řešilo pomocí přiřazení this do proměnné, která byla mimo danou scope:
class Contact {
  constructor(name, email, button) {
    this.name = name;
    this.email = email;
  	var that = this;
    button.onclick = function(event) {
  	  sendEmail(that.email);
    }
  }
}

Pokud ale funkci vytvoříme přes arrow zápis, tak máme this v kontextu konstruktoru té třídy:

class Contact {
  constructor(name, email, button) {
    this.name = name;
    this.email = email;
    button.onclick = (event) => {
      sendEmail(this.email);
    }
  }
}  

Defaultní parametry funkce

  • Dřívější verze ES5 nepodporovala nastavení defaultních parametrů funkce, řešilo se to přes zápis:
 function applyDiscount(cost, discount) {
     let discount = discount || 0.1; // 10% of discount;
     return cost - (cost * discount);
 }
 

S ES6 toho lze dosáhnout:

 function applyDiscount(cost, discount = 0.1) {
     return cost - (cost * discount);
 }   
 

Rest Parameters (...)

  • Jak lze přistupovat k argumentům funkcí?
 var sum = function() {
     var result = 0;
     for (var i=0; i < arguments.length; i++) {
         result += arguments[i];
     }
     return result;
 }
 sum(1, 2, 3, 4, 5); // 15
 

Samozřejmě můžeme k argumentům přistoupit jako k poli, ale všechny parametry jsou přiřazeny do proměnné arguments a nemůžeme je nijak odlišit.

S ES6 můžeme do funkce poslat kolik parametrů jen chceme

 const sum = (...numbers) => 
     numbers.reduce((acc, current) => acc + current, 0)     
 sum(1, 2, 3, 4, 5) // 15
 

Klidně i odlišné hodnoty:

 const sum = (start, ...numbers) =>     
     numbers.reduce((acc, current) => acc + current, start) 
 sum(10, 1, 2, 3, 4, 5) // 25    
 

Spread Parameters

  • Lze dělat i inverzi rest parametru - pošleme do funkce parametr, který bude přiřazen parametrům funkce.
 function sum(x, y) { 
     return x + y;
 }
 let nums = [1, 2];
 sum(...nums); // 3

Nebo:

 var runners = ["Mary", "Andrew", "Craig", "Michael", "Kenneth", "Dave"];
 function getLosers(first, second, third, ...losers) {
   return losers;
 }
 getLosers(...runners);  
 

Template Strings (obrácená uvozovka)

  1. Lze použít k tomu, abychom se vyhnuli zápisu řetězce, který má být na více řádků, pomocí oddělováíní řetězců a spojováním jich pomocí operátoru +
  2. Nebo také jako alternativa ke spojování řetězců pomocí plusek
 var element = [
     '<div>' +
         '<span>hey jude!</span>' + 
     '</div>'
 ].join('');

Lépe napsat jako:

 let element = `
     <div>
         <span>hey jude!</span>
     </div>
 `;
 

Spojování proměnné v řetězci:

 const name = 'We are the champions';
 let element = `<span>${name}</span>`;   
 

Operace s objekty

Destructuring

 let person = {
     name: 'John',
     lastName: 'Oliver'
 }
 let {name, lastName} = person;
 console.log(name); // John
 console.log(lastName); // Oliver

Lze to použít i přímo v rámci parametrů funkce:

 function greet({name, age}) {
     return 'Hey ${name}. ${age} is a good age.'
 }
 greet({
     name: 'Jason',
     age: 27
 });
 

A lze i vynechávat ty parametry, které náz nezajímají:

 let [,,third] = [1, 2, 3];
 console.log({third});         
 

Nebo těm původním parametrům nastavovat jiná jména:

 let {first: fn, last: ln} = person;
 console.log(fn, ln);   
 

Zkrácený zápis přiřazování proměnných do objektu

Takto bych to zapsal v ES5:

 var name = 'Freddie';
 var lastName = 'Mercury';
 var person = {
     name: name,
     lastName: lastName
 }
 

Takto to mohu zapsat nově v ES6:

 
 var name = 'Fredie';
 var lastName = 'Mercury';
 var person = {name, lastName};
 

Zkrácený zápis funkce v rámci objektu

Dříve:

 var person = {
     name: 'Henry',
     greet: function() {
          return 'Hello, ' + this.name;
     }
 }

Nyní:

 let person = {
     name : 'Henry',
     greet() {
          return `Hello, ${this.name}`
     }
 }
 

Class - práce s třídami

  • třídy podobně jako funkce mohou být buď class declaration nebo class expression (přiřazení do proměnné)
  • oproti např. Javě mohou obsahovat metody ale už nikolik objektové properties
  • mohou mít statické metody, gettery a settery

V ES5 se do “třídy” přidávala metoda takto:

 function User(name, email) {
     this.name = name;
     this.email = email;
     this.changeEmail = function(newEmail) {
         this.email = newEmail;
     }
 }
 

Pokud člověk nechtěl, aby funkce changeMail byla předeklarována pro každou instanci User, mohl to specifikovat jako prototyp.

Jaký je vlastně rozdíl mezi this a tím prototypem?

  • Pokud je v našem zájmu sdílet společnou funkcionalitu mezi objektovými instancemi, měli bychom používat prototype.
  • Je pak snadnější modifikovat společnou logiku ZA BĚHU i poté, co jsou instance již vytvořeny. Staré i nové objekty budou využívat aktualizovaný kód (což se nemusí podařit pokud použijeme this přístup)

V rámci ES6 je to podobné jako v Javě, jde to přímo:

 class User {
     constructor(name, email) {
         this.name = name;
         this.email = email;
     }
     changeEmail (email) {
         this.email = email;
     } 
 }
 

Dědičnost, gettery a settery

  • v ES6 lze definovat gettery a settery u metod:
 class Animal {
   constructor(name) {
       this._name = name;
   }
   get name() {
       return _name;
   }
   set name(name) {
       this._name = name;
   }
 }
 

Použití:

 
 const animal = new Animal('leopard');
 console.log(animal.name); // getter : leopard
 animal.name = 'lion';     // setter
 console.log(animal.name); // lion
        

V ES6 lze mít i poděděné objekty, dělá se to úplně stejně jako v Javě. Pro vyvolání konstruktoru nebo funkce předka se používá se klíčové slovo super(). Když chci mít potomka, který bude dědit od předka, klasicky použiju extends.

Moduly

  • psát moduly znamená, že naše funkce a proměnné nebudou přístupné dokud je explicitně neexportujeme a neimportujeme si je tam, kam chceme.

Funkci si lze exportovat (lze si exportovat i více funkcí):

 export function addHashtag(value){
     return `#${value}`;
 }
 export function somethingElse() {
     return "baf";
 }
 

A importovat:

 import { addHashtag, somethingElse } from './code.js'
 addHastag('javascript') // #javascript  
 

Pokud bychom chtěli exportovat pouze jednu jednou věc, použijeme klíčové slovo default:

 export default function addHashtag(value){
     return `#${value}`;
 }
 

A import:

 import addHashtag from './code.js'
 addHastag('javascript'); // #javascript       
 

Promises

  • O promisách toho vím už celkem dost, ale přijde mi, že šikovná věc může být ta, že se odchytí chyba v té jedné konkrétní promise:
 let fetchJSON = (url) { 
     return new Promise((resolve, reject) => { 
         $.getJSON(url)
              .done((json) => 
                   resolve(json)) 
              .fail((xhr, status, err) => 
                   reject(status + err.message)); 
     }); 
 }
 

Nyní se pokusíme získat JSON soubor z dané URL:

 
 fetchJSON('http://...')
     .then(res => 
               alert(JSON.stringify(res)), // if ok, alert content
           err => 
               console.err(err)) // if error, show on console

Promise.all()

  • Promisa nebude vyvolána, dokud nebudou vyvolány všechny promisy v poli:
 // get multiple JSON files
 const itemUrls = {'http://...', 
              'http://...'   };   
 const itemPromises = itemUrls.map(fetchJSON);  
 Promise.all(itemPromises)
     .then(function(results) {      
           // we just reach here if ALL Promises are done      
           results.forEach((item) => {        
                 // process item      
           });   
      })   
     .catch(function(err) { 
        // we reach here if any Promise fails
        console.log("Failed:", err);   
     });

Generátory

  • Normální funkce mají tu vlastnost, že dle očekávání běží “run to completition”.
  • Pokud vyvoláme vykonání funkce, pojede ta funkce, která se dokončí a poté se může vykonávat zase jiná funkce.
  • Generátory umožňují, aby se funkce v určitém bodě zastavila a později pokračovala ve vykonávání tam, kde skončila. Pak se třeba znovu zastavila, pokračovala, zastavila atd.
  • Při vytváření generátoru se používá speciální symbol hvězdičky function *numbers() {…}
  • Pro návrat z funkce se používá klíčové slovo yield
 function *numbers() {
     console.log('The magic has started');
     yield 1;
     yield 2;
     yield 3;
 }
 const iterator = numbers();   
 console.log(iterator.next()); // {value: 1, done: false}
 console.log(iterator.next()); // {value: 2, done: false}
 console.log(iterator.next()); // {value: 3, done: false}
 console.log(iterator.next()); // {value: undefine, done: true}
 

Přes generátor se dá i iterovat:

 for( let i in iterator) {
     console.log(i); // it will print:
                     // 1
                     // 2
                     // 3
 }
 

Nebo lze použít spread operátor:

 console.log([...numbers()]); // [1, 2, 3]      
 

Množiny (Sets)

 
* kolekce s unikátními položkami
 const items = new Set(['itemA', 'itemB', 'itemC', 'itemA', 'itemB', 'itemC']);
 console.log(items); // Set{‘itemA’, ‘itemB’, ‘itemC’}
                  // it ignores the repeated items   
                  

Kolekce mají metody:

  • add()
  • delete()
  • has()
  • forEach()
  • size
  • clear()

Metody pole

map()

There are very common things we want to do to lists. The first one that comes to mind is cycling through the list and performing an operation on each item. You can do this with Array.forEach.

var numbers = [1,2,3,4,5,6,7,8,9,10];  
numbers.forEach((n, index) => {  
   numbers[index] = n + 1;
});
// => [2,3,4,5,6,7,8,9,10,11]

Here, we're looping through the array and adding one to each item. This approach also has the side effect that it mutates the original list of data. As good developers, we want to reduce side effects and be transparent and idempotent with our functions and processing.

So, let's use map() to perform the same operation and leave the original list in tact.

var plusone = numbers.map((n) => n+1);  
// => numbers: [1,2,3,4,5,6,7,8,9,10]
// => plusone: [2,3,4,5,6,7,8,9,10,11]	

Now, what if we then want to take just the even numbers from this result in a list? Here comes the filter()!

filter()

We can use Array.filter() to visit each item in a list, much like map(); however, the predicate function you pass to filter() should return either true, to allow that item in the list, or false to skip it. Also like map(), filter() returns a new array with copies of the items that match the filter and does not modify the original.

var evens = plusone.filter((n) => n % 2 === 0);  
// => evens: [2,4,6,8,10]	

Even easier! Now that we've got the list we want to work with, lets get a count of the number of how many are evenly divisible by 4.

Because map() and filter() return new arrays, we can take advantage of this and chain multiple array operations together.

[1,2,3,4,5,6,7,8,9,10]
  .map((n) => n*2)
  .filter((n) => 10 % n == 0)
  .reduce((sum, n) => (sum += n), 0);
// => 12

reduce()

When we want to aggregate data in a list, or data related to a list, we can use Array.reduce(). reduce() applies a function to an accumulated value on each element in a list from left to right.

 var byfour = evens.reduce((groups, n) => {  
   let key = n % 4 == 0 ? 'yes' : 'no';
   (groups[key] = groups[key] || []).push(n);
   return groups;
 }, {});
 // => byfour: { 'yes': [4,8], 'no': [2,6,10] }	

Unlike map() and filter(), however, reduce() doesn't return a new list, it returns the aggregate value directly. In the case of our number list, our accumulated value was the initial empty object passed as the last parameter to the reduce() call. We then used that to create a property based on the divisible-by-four-ness of each number that served as a bucket to put the numbers in.

from()

The new .from() method is a static method of Array and allows us to create real arrays from “array-like” objects (such as arguments or dom collections); and it also allows us to pass a function to to apply to items in those arrays, giving us some .map()-like behavior as well.

For instance, we can create a real array from a DOM collection, which isn't really an instance of Array but is array like in that it allows indexing and has a length.

var divs = document.querySelectorAll('div.pane');  
var text = Array.from(divs, (d) => d.textContent);  
console.log("div text:", text);  

The above snippet takes the DOM collection returned from querySelectorAll() and uses Array.from to map across each item and return us a “real” array of the text content from those DOM elements. To use it with the arguments variable in functions is just as easy.

// Old, ES5 way to get array from arguments
function() {  
  var args = [].slice.call(arguments);
  //...
}
// Using ES6 Array.from
function() {  
  var args = Array.from(arguments);
  //..
}

find() a findIndex()

To get a value, if it exists in an array; and Array.findIndex() to get the index of something by value. However, instead of taking a value directly as a parameter, find() and findIndex take a predicate function that is applied to each element in the array, and once the predicate returns true, stops the search and returns the value at that position (for .find) or the index at that position (for findIndex).

// Array.find and Array.findIndex(fn)
var found = [1,4,-5,10].find((n) => n < 0);  
console.log("found:", found);
var index = [1,4,-5,10].findIndex((n) => n < 0);  
console.log("index:", index);
// found: -5
// index: 2	

of()

Array.of() lets us create a new Array instance from a variable number of arguments, regardless of the type of those arguments.

var arr = Array.of(0,true, undefined, null);  
console.log("arr:", arr);  
// arr: [0, true, undefined, null]

This is essentially the same as the following ES5 function:

function ArrayOf(){  
  return [].slice.call(arguments);
}	

for..of loop

The for..of loop creates a loop over any iterable object, including Array, arguments, Set, Map and custom iterable objects like generators. This is different than the for..in operator, as for..in iterates over an Array, you get indexes, not values. for..in can be used on Objects, but returns the property names, not values. for..of can not be used on Objects, as there is no default iterator defined for Objects in javascript.

var arr = [3,5,7,9],  
    obj = { a: 1, b: 2, c: 3 };
// ES5 for..in over objects
for (var p in obj) {  
  console.log(p);
}
// a  b   c
// ES5 for..in over arrays
for (var n in arr) {  
  console.log(n);
}
// 0 1 2 3
// ES6 for..of over arrays
for (let n of arr) {  
  console.log(n);
}
// 3 5 7 9 	

Using for..of, we can now actually iterate values on Arrays. Though we still can't use it on Objects, we can use a generator and for..of to loop over the keys and values in an object.

 // using a generator function
 function* entries(obj) {  
    for (let key of Object.keys(obj)) {
      yield [key, obj[key]];
    }
 }
 for (let [key, value] of entries(obj)) {  
    console.log(key, "->", value);
 }
 // a -> 1
 // b -> 2
 // c -> 3

ES7

Array.prototype.includes

  • Test na přítomnost položky v poli jsme prováděli jako array.indexOf(el) !== -1
  • Nyní lze na to použít funkci.
  • Syntax je element.includes(searchElement[, fromIndex])
 ['a', 'b', 'c'].includes('b');      // true
 ['a', 'b', 'c'].includes('d');      // false
 ['a', 'b', 'c'].includes('c', 2);   // true
 ['a', 'b', 'c'].includes('c', 3);   // false
 ['a', 'b', 'c'].includes('c', 'g'); // true
 ['a', 'b', NaN].includes(NaN);      // true

Funguje to i na řetězec:

 'a123b'.includes('123')             // true 
 

Exponentiation Operator (**)

  • normální matematický exponent

async/await

Zdroje

knowledge_base.txt · Last modified: 2017/04/23 20:16 by buddha