JavaScript: The Hard Parts

JavaScript: The Hard Parts

Pro starší a pokročilé

Ondřej Žára, @0ndras

Ještě než začneme…

Úvodem

Klíčové slovo this

let fun = function() { alert(this); } fun(); /* ? */ let obj1 = { fun:fun }; let obj2 = { fun:fun }; obj1.fun(); /* this == obj1 */ obj2.fun(); /* this == obj2 */ fun == obj1.fun == obj2.fun

Klíčové slovo this

let fun = function() { alert(this); } let obj1 = {}; let obj2 = {}; fun.call(obj1, arg1, ...); /* this == obj1 */ fun.apply(obj2, [arg1, ...]); /* this == obj2 */

Klíčové slovo this – bind

let f1 = function() { alert(this.name); } let foo = { name:"foo" } let bar = { name:"bar" } f1.call(foo); /* "foo" */ let f2 = f1.bind(bar); f2() /* "bar" */

Klíčové slovo this – bind

Klíčové slovo this – ES6 Arrow Functions

Klíčové slovo this – souhrn

  1. Arrow function: převzato z obalující funkce
  2. Partial application: zafixováno ve chvíli volání bind
  3. Volání s call/apply: hodnota prvního parametru
  4. Volání s tečkou: hodnota objektu před tečkou
  5. Jinak undefined

Prototypová vazba

let obj1 = { klic:"hodnota" }; let obj2 = Object.create(obj1); alert(obj2.klic); /* "hodnota" */ Object.getPrototypeOf(obj2) == obj1; // obj2.__proto__ == obj1;

Prototype chain

let obj1 = { klic:"hodnota" }; let obj2 = Object.create(obj1); let obj3 = Object.create(obj2); obj3.klic = "jina hodnota"; obj1.jinyKlic = "jeste jina hodnota"; alert(obj2.klic); /* "hodnota" */ alert(obj3.klic); /* "jina hodnota" */ alert(obj3.jinyKlic); /* "jeste jina hodnota" */

Prototypová vazba – tvorba objektů

V JS jsou tři způsoby tvorby objektů

  1. Ex nihilo: let x = {p: 3.14}
  2. S prototypovou vazbou: let x = Object.create(y)
  3. Operátorem new

Prototypová vazba – operátor new

Prototypová vazba – operátor new

diagram

let F = function() { this.a = 1; } F.prototype.b = 2; let f = new F(); f.c = 3;

Operátor new vlastnoručně

function myNew(ctor, ...args) { let inst = Object.create(ctor.prototype); let result = ctor.apply(inst, args); return (typeof(result) == "object" ? result : inst); }

Dědičnost pomocí prototypů

diagram

let Parent = function() {} Parent.prototype.hi = function() { return "hello"; } let Child = function() {} Child.prototype = Object.create(Parent.prototype); /* alternativně: Child.prototype = new Parent(); */ let ch = new Child(); ch.hi(); /* "hello" */

Obohacování prototypů

String.prototype.lpad = function(what, length) { let count = length - this.length; let padding = ""; for (let i=0; i<count; i++) { padding += what; } return padding + this; }

Obohacování prototypů – bind

Function.prototype.bind = function(thisObj, ...args1) { let fn = this; return function(...args2) { return fn.apply(thisObj, args1.concat(args2)); } }

Prototypy v ES6 – class

class Child extends Parent { doStuff() { super.doStuff(); } } /* ↑↓ */ function Child() {} Child.prototype = Object.create(Parent.prototype); Child.prototype.doStuff = function() { return Parent.prototype.doStuff(); }

Prototypy: odbočka

Nepodporujeme atribut prototype, který je u všech objektů. Tento atribut je normou povolen a přidává do objektu nové metody a atributy (případně s danou hodnotou). Tento atribut jsme neimplementovali, protože by byl problém ho implementovat a jsme toho názoru, že je k ničemu.

— prohlížeč Links, projektová dokumentace

Event loop

Event loop v pseudokódu

let scheduledJS = ""; let listeners = []; while (1) { eval(scheduledJS); /* TADY se vykoná JS */ if (!listeners.length) break; /* počkat, než bude čas na nejbližší posluchač */ let currentListener = waitFor(listeners); /* naplánovat jej */ scheduledJS = listeners[currentListener]; delete listeners[currentListener]; }

Event loop

Jak naplánovat další vykonávání JS?

Bind a setTimeout

Víte, co a proč udělá tato funkce?

function s(a) { while (a.length) { let v = a.shift(); setTimeout(a.push.bind(a, v), v); } }

Promise – elegantnější řízení toku kódu

Nevýhody callbacků

Promise – elegantnější řízení toku kódu

Promise – konzumace

let promise = doAsyncStuff(); promise.then(okCallback, errorCallback); promise.then(anotherCallback); // téměř se nepoužívá promise.then(x).then(y);

Promise – konzumace asynchronně

Metoda then() vrací novou Promise, závislou na návratové hodnotě callbacku

let promise = doAsyncStuff1(); promise.then(doAsyncStuff2).then(console.log);

Promise – konzumace chyb

doAsyncStuff().then(null, onError) doAsyncStuff().catch(onError) // finalize v obou případech, tj. chyba se nepropaguje doAsyncStuff().then(ok, err).then(finalize); // implicitní try-catch kolem vykonání cb doAsyncStuff().then(cb).catch(err);

Promise – tvorba

let pSetTimeout = function(delay) { let executor = function(resolve, reject) { setTimeout(resolve, delay); } return new Promise(executor); } pSetTimeout(3000).then(...);

Promise – tvorba

Promise – statické metody

Promise – asynchronní vykonání

Důvod: stejné chování synchronních i asynchronních funkcí!

// stejné jako setTimeout(() => console.log(42), 0) Promise.resolve(42).then(console.log); console.log("lolwut");

Prostor pro otázky