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