Pokazywanie postów oznaczonych etykietą es6. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą es6. Pokaż wszystkie posty

wtorek, 3 listopada 2015

[HTML|JS|CSS] ES6: Classes

Nowa specyfikacja EcmaScript wproawdza słowo kluczowe class, by znacznie uprościć budowanie obiektowych konstrukcji w JS. Do tej pory obiekty tworzyliśmy między innymi za pomocą konstruktorów (funkcja wołana ze słowem kluczowym new), natomiast za dziedziczenie odpowiadało property prototype. ES6 wprowdza klasy, aby uporządkować te mechanizmy. Jedynym celem, dla którego powinniśmy używać klas jest tworzenie obiektów.

Klasy możemy definiować na dwa sposoby:

// class declaration
class Car
{
  constructor(make)
  {
    this.make = make;
  }
}

var ford = new Car("Ford");
console.log(ford.make);

console.log(typeof Car); //function

// class expression syntax
var Plane = class {
  constructor(make)
  {
    this.make = make;
  }
}

var plane = new Plane("Airbus");
console.log(plane.make);

Poza konstruktorami, klasy mogą zawierać regularne funkcja (które zostaną dodane do prototypu obiektu), oraz settery i gettery pozwalające na zaimplementowanie reguł enkapsulacji (jak na przykład w C#). Specjalnym rodzajem metod są metody statyczne - nie zostaną one dodane do prototype, tylko do samej klasy.

class Tank
{
  constructor()
  {
    this._name = "";
  }

  get name(){
  	console.log("getting name");
  	return this._name;
  }

  set name(value){
  	console.log("setting name");
  	this._name = value;
  }

  fire(){
  	console.log("fire !");
  }

  static report(){
  	console.log("reporting !");
  }
}


var abrams = new Tank();
abrams.name = "Abrams";
console.log(abrams.name);
abrams.fire();
Tank.report();

console.log(typeof abrams.__proto__.fire); //function
console.log(typeof abrams.__proto__.report); //undefined

Klasy niosą za sobą także możliwość dziedziczenia, reprezentowaną przez słowo kluczowe extends. Dziedziczyć można po klasie lub bezpośrednio po konstruktorze. Drugie, nowe słowo kluczowe to super. Służy ono do wywołania konstruktora bazowego oraz do odwołania się do metod z bazowego konstruktora.

function A(a)
{
  this.a = a;
}

A.prototype.printA = function(){
  console.log(this.a);
}

class B extends A
{
  constructor(a, b)
  {
    super(a);
    this.b = b;
  }

  printB()
  {
    console.log(this.b);
  }

  static sayHello()
  {
    console.log("Hello");
  }
}

class C extends B
{
  constructor(a, b, c)
  {
    super(a, b);
    this.c = c;
  }

  printC()
  {
    console.log(this.c);
  }

  printAll()
  {
    this.printC();
    super.printB();
    super.printA();
  }
}

var obj = new C(1, 2, 3);
obj.printAll();

C.sayHello();
asdasd

niedziela, 1 listopada 2015

[HTML|JS|CSS] ES6: Promise

Pisanie asynchronicznego kodu nigdy nie było zbyt przyjemne w JS. Między innymi dlatego zdecydowano się na zaimplementowanie dobrze znanego wzorca Promise, jako natywnego mechanizmu JS. Od wersji ES6, obiekty Promise reprezentujące asynchroniczną operację, tworzone są konstruktorem. Funkcja, którą przyjmuje konstruktur powinna reprezentować asynchroniczną operację (np. AJAX-ową), zakonczoną wywołaniem callbacka resolve lub reject.

var timeoutPromise = new Promise(function(resolve, reject){

  setTimeout(function(){
   var d = new Date();
 var milis = d.getMilliseconds();
 if(milis % 10 === 0){
  reject("an error occured");
 }
 else{
  resolve(d);
 }
  }, 1000);

});

timeoutPromise.then(function(data){
 //onFulfilled
 console.log(data);
}, function(err){
 //onError
 console.log(err);
})


timeoutPromise.then(function(data){
 console.log(data);
 return "processed"; //returns next promise
}, function(err){
 console.log(err);
}).then(function(data){
 console.log(data);
});

Promise'y można tworzyć z synchronicznych obiektów metodami Promise.resolve, np. Promise.resolve(13). Dodatkowo przydatne są metody all i race, które pozwalają obsługiwać wiele asynchronicznych operacji. Pierwsza z nich zwróci callback, kiedy wszystkie promise'y zostaną wykonane, druga w przypadku, gdy wykona się którykolwiek z nich.

var p1 = new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve();
    console.log("p1");
  }, new Date().getMilliseconds());
});

var p2 = new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve();
    console.log("p2");
  }, new Date().getMilliseconds());
});


Promise.all([p1, p2]).then(function(){
  console.log("All done");
});

Promise.race([p1, p2]).then(function(){
  console.log("First done");
});

[HTML|JS|CSS] ES6: Iterators, generators

Obiekty, które zaimplementują iteration protocol nazywane są iteratorami. Obiekt taki musi posiadać metodę next, zwracającą obiekt z dwoma propercjami: value oraz done.
let fibonacci100Iterator = {
  last: 0,
  previous : 1,
  index: 0,
  next: function(){
   let value = this.last + this.previous;
   this.previous = this.last;
   this.last = value;

    return {done: this.index++ > 100, value: value};
  }
};

while(true){
 let {done, value} = fibonacci100Iterator.next();
 console.log(value);
 if(done)
  break;
}

ES6 wprowadza nowy typ danych: Symbol. Od teraz obiekty mogą mieć klucze typu string lub właśnie Symbol. Zadaniem funkcji Symbol() jest wygenerowanie unikalnego identyfikatora tak, by uniknąć kolizji wartości. Obiekt Symbol zawiera kilka predefiniowanych propercji będących symbolami dla różnych konstrukcji ES6. Jedną z nich jest Symbol.iterator.

Obiekty, które pod kluczem Symbol.iterator będą miały zdefiniowaną funkcję zwracającą iterator nazywane będą iterable. Każdy iterable może być odpytywany za pomocą nowej pętli for ..of.

let iterableFib = {
 [Symbol.iterator] : function(){
  return fibonacci100Iterator;
 }
};

for(let val of iterableFib){
 console.log(val);
}

 Kolejną konstrukcją są generatory, czyli funkcje zwracające wiele wartości, jedna po drugiej. Obiekt generatora nie wykonuje się od razu, lecz zwraca obiekt implementujący iterable. Z każdym wywołaniem metody next wykonywana jest ona do napotkania pierwszej instrukcji yield. Wtedy wykonanie zostaje zatrzymane, aż do kolejnego wywołania next.

function* fibonacci_generator()
{
  let last = 0;
  let previous = 1;
  let index = 0;

  while(index++ < 100){
   let value = last + previous;
   previous = last;
   last = value;
   yield value;
  }
}

sobota, 31 października 2015

[HTML|JS|CSS] ES6: Strings, arrays, collections

Strings

Poza kilkoma dodatkowymi metodami na obiekcie stringa, takimi jak includes, startsWith, endsWith, znanymi z innych języków programowania, najważniejsza zmiana to tzw. template strings - wyrażania opakowane w backticki, służące do wygodnego budowania stringów.

let a = 20;
let b = 6;
let c = "JavaScript";
let str = `My age is ${a+b} and I love ${c}`;

console.log(str);

Arrays 

Metoda from przypomina select z C# z możliwością wstawienia opcji jako trzeci parametr. Dodatkowo wprowadzono metodę fill do wstawiania danej wartości w istniejącej już tablicy (z możliwością zdefiniowania granic wstawiania). Kolejne ważne metody to find oraz findIndex przyjmujące predykat.

Collections

W ES5 mieliśmy do dyspozycji tylko tablice. Teraz dochodzą nowe kolekcje, takie jak:
  • Array buffer - wydajna kolekcja przechowująca liczby, nie może rosnąć dynamicznie
  • Typed array - wrapper na array buffer pozwalający w prosty sposób czytać z niej i zapisywać obiekty
  • Set - kolekcja unikalnych wartości dowolnego typu, powtarzające się wartości są wyrzucane
  • Weak Set - w odróżnieniu od set przechowuje jedynie referencje do obiektów
  • Map - kolekcja par klucz  wartość
  • Weak Map - w odróżnieniu od map kluczami mogą być jedynie referencje obiektów

let set = new Set("Hello!!!");
set.add(12);
console.log(set.has("!")); //check if value exists
console.log(set.size);
set.delete(12);
console.log(...set); //H e l o !
set.clear();

let map = new Map();
let o = {n: 1};
map.set(o, "A"); //add
map.set("2", 9);
console.log(map.has("2"));
console.log(map.get(o));
console.log(...map);
map.delete("2"); //remove key with value
map.clear();

[HTML|JS|CSS] ES6: Syntax

Nowa specyfikacja ECMAScript wnosi sporo nowości do JavaScriptu. Nie możemy ich jeszcze w pełni wykorzystywać bez potrzeby transpilacji, ale kwestią czasu jest, kiedy przeglądarki zaimplementują wszystkie nowości w swoich engine'ach. W tym momencie do transpilacji można używać np. babeljs, będącego pakietem node.js. Instalujemy go poprzez wywołanie

npm install -g babel-cli
npm install --save-dev babel-preset-es2015
babel --presets es2015 script_es6.js --out-file script_es5.js

Ostatnie polecenie reprezentuje transpilację z jednego pliku do drugiego. Od kilku dni należy do tego celu wykorzystywać preset es2015, wcześniej transpilacja była domyślną operacją.

Najważniejsze zmiany składniowe w ES2015 (ES6):

let keyword

Deklaruje zmienną typu block-scoped, co jest naturalne w większości języków programowania, w JS nie było, kiedy mieliśmy do wyboru tylko var (function-scoped variables).

function myFunction()
{
  console.log(a);

  let b = 13; //accessible throughout function
  console.log(b);

  if(true)
  {
    let c = 14; //accessible throughout function
    let b = 15;
    console.log(b);
  }

  //console.log(c); //c is not defined
}

Zmienne możemy nadpisywać poprzez let, ale tylko jeżeli są w innym block-scope. Zaleca się używanie let zamiast var, ze względu na zmniejszenie podatności na bugi takiego kodu.

const keyword

Zmienne typu read-only, ostatnia linijka zwróci SyntaxError podczas transpilacji, a docelowo także błąd w run-time. Zmienne typu const są również block-scoped.

const pi = 3.141;
var r = 2;

console.log(pi * r * r); //Output "12.564"

pi = 12; //throws read-only exception

 W przypadku obiektów deklarowanych jako const będziemy mogli modyfikować ich propercje, a błąd dostaniemy tylko przy próbie nadpisania całego obiektu.

default parameter values

W ES5 nie musieliśmy przekazywać wszystkich argumentów wywołując funkcję. Nieprzekazane zmienne przyjmowały wartość undefined. Teraz możemy od razu zadeklarować dla każdego parametru domyślne wartości.

function myFunction(x = 1, y = 2, z = 3){
 console.log(x, y, z); //Output "6 7 3"
}

myFunction(6, 7);

spread operator

Operator ten reprentują ... , a jego zadaniem jest rozdzielenie obiektu typu iterable na pojedyncze elementy. Po transpilacji otrzymamy metodę apply (przyjmuje listę parametrów jako tablicę) wołaną na funkcji, ale docelowo wykorzystane będą znacznie szybsze mechanizmy w runtime.

function myFunction3(a, b)
{
 return a + b;
}

let data = [1, 4];
let result = myFunction3(...data);
console.log(result); //Output "5"

Można go wykorzystać także przy budowaniu tablicy z innej tablicy.

rest parameter

Trzech kropek można także użyć do obsługi funkcji ze zmienną liczbą argumentów. Rest parameter zmapuje nadmiarowe zmienne jako tablicę.

function myFunction(a, b, ...args)
{
  console.log(args); //Output "3, 4, 5"
}

myFunction(1, 2, 3, 4, 5);

array destructuring assignment

Pozwala na przypisanie wartości do wielu zmiennych za pomocą jednego wyrażenia. Prawą stroną musi być obiekt typu iterable. Array destructuring pozwala na ignorowanie niektórych elementów oraz użycie spread operator-a.

let myArray2 = [1, 2, 3];
let [g, h, i] = myArray2;
let [j,  , k] = myArray2;
let [l, ...m] = myArray2;

object destructuring assignment

W analogiczny sposób możemy wykonywać destructuring na obiektach.

let object = {"name" : "John", "age" : 23};
let {name, age} = object; //object destructuring assignment syntax

arrow function

Przypomina dobrze znane np. z C# lambda expression. Więcej niż jedną linię należy opakowywać w nawiasy klamrowe.

let circleArea = (pi, r) => pi * r * r;
let result = circleArea(3.14, 3);

let circleArea2 = (pi, r) => {
 let area = pi * r * r;
 return area;
};
let result = circleArea2(3.14, 3);


enhanced object literals

Wprowadzono kilka udogodnien składniowych przy budowie obiektów. Mamy do dyspozycji skróconą składnię przy definiowaniu propercji (również tych z wyliczanymi nazwami) oraz metod, 

let x = 1, y = 2;
let object = { x, y };

console.log(object.x); //output "1"

let object = {
  ["first" + "Name"]: "Eden",
};

//extract
console.log(object["first" + "Name"]); //Output "Eden"

let object = {
  myFunction(){
    console.log("Hello World!!!"); //Output "Hello World!!!"
  }
}