ES6 "Up To Speed"

https://twitter.com/btmpl/

https://medium.com/@baphemot/

Bartosz Szczeciński

const, let

if(true) {
  var a = 42;
  let b = 42;
}
console.log(a); // 42;
console.log(b); // błąd - b nie jest zdefiniowane

const a = 42;
a = 43; // błąd - nie można zmienić przypisania

const b = { theAnswerToLifeTheUniverseAndEverything: undefined }
b.theAnswerToLifeTheUniverseAndEverything = 42; // poprawnie

const c = { theAnswerToLifeTheUniverseAndEverything: undefined };
Object.freeze(c);
c.theAnswerToLifeTheUniverseAndEverything = 42; // błąd

const d = [];
d.push(42); // poprawnie

- nowe typy zmiennych

- zasięg lokalny

- brak możliwości ponownej deklaracji

- const - brak możliwości ponownej definicji 

const, let

// wyświetli dwa razy "2"
for(var i = 0 ; i < 2 ; i++) {
  setTimeout(function() { console.log(i) });
}              

// wyświetli "0" i "1"
for(let i = 0 ; i < 2 ; i++) {
  setTimeout(function() { console.log(i) });
}   

for(let i = 0 ; i < 2 ; i++) {
  elementCollection[i].addEventListener('click', function() { alert(i) });
} 

- przechwytywane przez closure

arrow functions

[1, 2, 3].map(function(v) { return v * 2; });

// to samo co
[1, 2, 3].map((v) => { return v * 2; });

// i krócej
[1, 2, 3].map(v => v * 2);

- krótsza notacja

- domyślne return dla jedno wyrażeniowych funkcji

arrow functions

function a() {
  setTimeout(function() { console.log(this) })                
}
new a(); // Window lub undefined w strict

function b() {
  setTimeout(() => { console.log(this) });
}
new b(); // b{}

function c() {
  setTimeout(function() { console.log(this) }.bind(this))                
}
new b(); // c{}

- `this` przypisane w momencie deklaracji

- przydatne przy handlerach zdarzeń, przy pracy z klasami

parametry domyślne

function test(a, b = 42, c) {
  console.log(a, b, c);
}
test(); // undefined, 42, undefined

- możliwość definiowania parametrów domyślnych

- można definiować wybrane parametry, pozostawiając
  kolejne bez wartości domyślnej

destrukturyzowanie

function testObject({ jeden = 42 }) {
  console.log(jeden);
}

testObject({jeden: 1, dwa: 2}); // "1"
testObject({dwa: 2}); // "42"


function testArray([jeden]) {
  console.log(jeden);
}
testArray(["jeden", "dwa"]); // "jeden"


function testReturn() {
  return [1,2,3];
}

const [,,trzy] = testReturn();
console.log(trzy); // "3"

- wyciąganie wartości ze złożonych typów danych

- można użyć w definicji funkcji

operator rest

function test(...input) {
  console.log(input);
}              
test(1,2,3); // [1,2,3]

function testOgraniczony(pierwszy, ...input) {
  console.log(pierwszy, input);
}
testOgraniczony(1,2,3); // 1, [2,3]


function testReturn() {
  return [1,2,3];
}

const [one, ...rest] = testReturn();
console.log(one, rest); // 1, [2,3]

- przechwytuje "resztę" danych

template strings

const a = `klasyczny łańcuch tekstowy`;
const b = `łańcuch tekstowy
w wielu liniach`;

const c = "łańcuch z osadzoną zmienną: " + a + "!";
const d = `łańcuch z osadzoną zmienną: ${a}!`;

- proste tworzenie stringów z wieloma liniami

- interpolacja danych

tagged template strings

function logToConsole(input) {
  console.log("Loguję: ", input);
}            

logToConsole`Witaj świecie!`; // "Loguję: ", ["Witaj świecie"];

const Component = styled.div`
  color: 'red'
`;

- wywoływanie funkcji z łańcuchem jako parametr

klasy

function Person(firstName) {
  this.firstName = firstName;
}
Person.prototype.hello = function() {
  console.log("Hello, my name is" + this.firstName + "!");
};
var me = new Person("Bob");
me.hello();

- wywoływanie funkcji z łańcuchem jako parametr

class Person {
  
  constructor(firstName) {
    this.firstName = firstName;
  }
  
  hello() {
    console.log("Hello, my name is " + this.firstName + "!");
  }
}

const me = new Person("Bob");
me.hello();

klasy

- getters, setters

class Person {
  
  // jezeli użyjemy this.firstName tutaj, wpadniemy w pętlę!
  set firstName (value) {
    this._firstName= value;
  }

  get firstName () { 
    return this._firstName ? this._firstName: 'Anonim';
  }
  
  hello() {
    console.log("Hello, my name is " + this.firstName+ "!");
  }
}

const me = new Person();
me.firstName = "Bob"
me.hello();

moduły

- logiczny podział kodu

- dystrybucja

 

common.js:

// modul.js

const MyConst = 42;

const myFunction = () => {
  // skomplikowany kod
}

module.exports = {
  MyConst: MyConst
}

module.exports.default = myFunction;

// app.js

const otherFunction = require('./modul.js').default;
const otherConst = require('./modul.js').MyConst;

moduły

ES6 modules:

// modul.js

export const MyConst = 42; // "named export"

export default () => { // "default export"
  // skomplikowany kod
}

// app.js

import otherFunction from "./modul.js";
import { MyConst } from "./modul.js";

- składnia jest podobna do destrukturowania ale to nie to samo!

- moduły działają jak singleton

moduły

- moduły nie zaczynające się od znaku specjalnego (poza @)
  "wyszukiwane" są w node_modules

import React from "react"; // wyszukiwany w node_modules/react/index.js

- moduły zaczynające się od znaku specjalnego - kropki lub /
  wyszukiwane są relatywnie

- możliwość nadpisania w konfiguracji webpack

- moduły zaczynające się od "@" to tzw. "scoped modules",
  pomaga to w uporządkowaniu kodu dużych bibliotek

import router from "@angular/router"; // wyszuka w node_modules/@angular/router/index.js

moduły - częste pomyłki

- import domyślny w miejscu nazwanego i odwrotnie

- nazwanie projektu tak samo jak nazwa modułu

- brak zablokowania wersji (shrinkwrap, lock)

object shorthand notation, computed properties

const test = 5;

const obj = {
  test: test
}

const obj2 = {
  test
}
const name = "test";

const obj = {
  [name]: 42
}

console.log(obj.test); // "42"

object setters, getters

const obj = {
  get name () {
    return this._name;
  },
  
  set name (name) {
    this._name = name;
  }
}

obj.name = 'Bob';
console.log(obj.name);

spread operator

const source = [1, 2, 3];
const copy = source;
copy.push(4);
console.log(copy); // [1,2,3,4];
console.log(source); // [1,2,3,4]; :(

const shallowCopy = [...source];
shallowCopy.push(5);
console.log(shallowCopy); // [1,2,3,4,5];
console.log(source); // [1,2,3,4]; :)
const source = [{
    test: 1
}];

const shallowCopy = [...source];

shallowCopy[0].test = 42;

console.log(shallowCopy[0].test); // 42
console.log(source[0].test); // 42 :(

spread operator

const source = {
  test: 1
}
const copy = { ...source };
const source = [{
    test: 1
}];

const copy = [...source];
copy[0] = {...copy[0]}
copy[0].test = 42; 

console.log(source[0].test); // 1
console.log(copy[0].test); // 42

stage-3 proposal, działa w CRA

co z wielo-wymiarowymi obiektami?

Promises

- "callback hell"

fetch('http://json.example', (response) => {
  response.json((data) => {
    console.log(data);
  }, (error) => {
    console.log(error);
  })
}, (error) => {
  console.log(error);
})

Promises

fetch('http://json.example')
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log(error));

- podstawa pracy z kodem asynchronicznym

- jeden z 3 stanów: nie rozwiązana, rozwiązana, odrzucona

Promises

- Promise.resolve(), Promise.reject()

- Promise.all(arrayOfPromises).then() - wszystkie Promise

- Promise.race(arrayOfPromises).then() - jakakolwiek

Promise.all([
  fetch('http://example.json'),
  fetch('http://example.json/2')
]).then(data => {
  // data[0], data[1]
});

Promises - częste pomyłki

- traktowanie kodu jak kod synchroniczny

const getData = () => {
  let data;
  fetch('http://example.com')
    .then(res => res.json())
    .then(d => data = d);

  return data;
}

const remoteData = getData();
console.log(data); // undefined
const getData = () => {
  let data; // 2
  fetch('http://example.com') // 3
    .then(res => res.json()) // 6
    .then(d => data = d); // 7

  return data; // 4
}

const remoteData = getData(); // 1
console.log(data); // 5

Promises

async/await ("ES8")

- rozwiązuje problem "promise hell"

- składnia podobna do języków synchronicznych

async function getData() {
  const response = await fetch('http://example.json')
  const json = await response.json();
  console.log(json);
}

async/await ("ES8")

- "Promise w przebraniu"

- "infekuje" kod

async function getData() {
  const response = await fetch('http://example.json')
  const json = await response.json();
  return json;
}

// ŹLE
console.log(getData()); // [object Promise] { ... }

// DOBRZE
getData().then(data => console.log(data);

async/await ("ES8")

dekoreatory (nie ES6)

- używane przez popularne biblioteki (MobX, react-dnd)

- modyfikują działanie metod klasy

const time = (target, name, descriptor) => {
  const original = descriptor.value;
  // Uzyj function by umożliwić późne wiązanie this
  descriptor.value = function(...input) {
    console.time(name);
    original.apply(this, ...input)
    console.timeEnd(name);
  }
}

class Demo {                
  @time
  member() {
    console.log('Wywołano metodę "member"');
  }
}

const obj = new Demo;
obj.member();
/**
 * Wywołano metodę "member"
 * member: 0.203857421875ms
 */

dekoreatory (nie ES6)

- używane przez popularne biblioteki (MobX, react-dnd)

- modyfikują działanie metod klasy

const readonly = (target, name, descriptor) => {
  descriptor.writable = false;
}

class Demo {                
 
  @readonly
  a = 5;
}

obj.a = 1;
console.log(obj.a); // 5

ES6

By btmpl

ES6

  • 435