What's new in ES2019
Chayanika Khatua
"Any application that can be
written in JavaScript, will
eventually by written in
JavaScript."
- Jeff Atwood, Co-founder of StackOverflow
2 / 48
We are 10.7 million developers
worldwide.
Together we support over 4.1 billion
users of the web.
3 / 48
Me, back in the day
Arrays
for (var i = 0; i < students.length; i++) {
newStudents.push(transform(students[i]));
}
Strings
...we write JavaScript very differently now.
var message = name + ', You have $' + dollars + ' in your account.
4 / 48
Me, now
5 / 48
What we're talking about
ECMAScript, a brief, not at all complete, history
So, what is new in ES2019?
How do proposals turn into features?
ESNext proposals
6 / 48
The year was 1995
7 / 48
Mocha is born
Brendan Eich creates
Mocha in 10 days
Java is really new and
trendy. JavaScript is
marketed as the
lightweight cousin.
Eventually renamed to
JavaScript in December
1995
8 / 48
JScript
Microsoft releases JScript in August 1996.
9 / 48
ECMA
Netscape approaches the European Computer Manufacturers
Association.
JavaScript and JScript are implementations.
The spec is called ECMAScript.
ECMAScript 1 released in 1997.
10 / 48
ES2019 Features
Array.prototype.flat
Flattens an array according to the depth specified.
const arr = [[1, 2], [3, 4, 5]];
const flattened = arr.flat(); // default depth is 1
// flattened = [1, 2, 3, 4, 5]
Specifying the depth:
const arr = [[1, 2], [[3]]];
const flattened = arr.flat(2);
// flattened = [1, 2, 3]
Arbitrary depth? Use Infinity.
12 / 48
Use case
const animals = await Promise.all([
getWildAnimals(),
getWingedCreatures(),
getPets(),
]);
// animals = [[ 🦊 , 🦁 ], [ 🐥 , 🦢 ], [ 🐱 ]];
const flattenedAnimals = animals.flat();
// flattenedAnimals = [ 🦊 , 🦁 , 🐥 , 🦢 , 🐱 ]
13 / 48
Array.prototype.flatMap
With map() every element is translated to exactly one output
element.
With flatMap() every element can be transformed into a variable
number of elements.
14 / 48
Working with data
// Folder = { files: [] }
const folders = [folder1, folder2, ...];
const toFiles = folder => folder.files;
// Alternative
const result = folders.map(toFiles).flat(1);
const result = folders.flatMap(toFiles);
// result = [file1, file2, file3, ...];
// Before flatMap, not performant
const result = folders.reduce(
(acc, folder) => acc.concat(folder.files)
, []);
15 / 48
Mapping and filtering
const numbers = [ 1, 2, 3, 4 ];
const doubleEven = num => {
if (num % 2 === 0) {
return [ num * 2 ];
}
return [];
};
const filteredAndDoubled = numbers.flatMap(doubleEven);
// filteredAndDoubled = [ 4, 8 ];
16 / 48
Array.prototype.smoosh?
17 / 48
Object.fromEntries
Counterpart to the Object.entries.
const obj = Object.fromEntries([
['a', 1],
['b', 2]
]);
// obj = {a: 1, b: 2}
console.log(Object.entries(obj));
// [ ['a', 1], ['b', 2] ]
18 / 48
Object transformation
const objectOG = { a: 1, b: 2, c: 3 };
const resultObject = Object.fromEntries(
Object.entries(objectOG)
.map(([ key, val ]) => [ key, val * 2 ])
);
// resultObject = { a: 2, b: 4, c: 6 }
Alternative, pre 2019
const result = Object.keys(objectOG).reduce((acc, key) => {
acc[key] = objectOG[key] * 2;
return acc;
}, {});
19 / 48
Maps and Objects
const map = new Map([
['a', 1],
['b', 2]
]);
const obj = Object.fromEntries(map);
// obj = {a: 1, b: 2}
20 / 48
Optional catch binding
Pre 2019
try {
// Try something dangerous
} catch (error) {
logger.error(error);
}
Post 2019
The error parameter can be dropped.
try {
data = JSON.parse(response);
} catch {
data = defaultData;
}
21 / 48
Array.prototype.sort stable
const arr = [
{ name: 'C', value: 90 },
{ name: 'A', value: 80 },
{ name: 'B', value: 80 },
];
// compareValueFunction compares the value property and should res
arr.sort(compareValueFunction);
// pre, possibly
[
{ name: 'B', value: 80 },
{ name: 'A', value: 80 },
{ name: 'C', value: 90 },
];
// post, required
[
{ name: 'A', value: 80 },
{ name: 'B', value: 80 },
{ name: 'C', value: 90 },
];
22 / 48
Recap ES2019
Array.prototype.{flat, flatMap}
Object.fromEntries
Optional catch binding
Array.prototype.sort stable
All these are supported in all modern browsers and Node 12+
23 / 48
The Evolution of ECMAScript
The TC39 proposal process
Technical committee 39 of ECMA International
ECMA 262 specification
Members are implementers that send delegates
Committee meets six times a year
A new process was defined after ES4 was abandoned, and ES6
was a massive release that took 6 years.
25 / 48
Stage 0 (Strawperson)
Format to allow input into the specification
Planned or presented to TC39
26 / 48
Stage 1 (Proposal)
Identified champion or champions who will advance the
addition.
Describe the problem or requirement
High level API
Potential concerns have been identified
27 / 48
Stage 2 (Draft)
Initial spec text
Incremental changes expected
The committee expects the feature to be developed and
eventually included in the standard
28 / 48
Stage 3 (Candidate)
At this stage, the proposal is considered complete
No further work is possible without implementation experience
Only changes considered critical based on implementation
experience
29 / 48
Classes right now
Classes were added in ES6
class Counter {
constructor() {
this.min = 0;
this.max = 100;
this._count = 0;
}
get count() {
return this._count;
}
increment() {
this._count++;
}
decrement() {
this._count--;
}
}
Counter.someStaticProp = 'Static';
30 / 48
As a User
const counter = new Counter();
console.log(counter.count); // 0
counter._count = 42;
console.log(counter.count); // 42
31 / 48
Class Fields (Stage 3)
class Counter {
static someStaticProp = 'Static';
this.min = 0;
this.max = 100;
this._count = 0;
get count() {
return this._count;
}
increment() {
this._count++;
}
decrement() {
this._count--;
}
}
Supported in Chrome 72, Node 12
32 / 48
Privacy matters (Stage 3)
class Counter {
#count = 0;
get count() {
return this.#count;
}
increment() {
this.#count++;
}
decrement() {
this.#count--;
}
}
33 / 48
As a User
const counter = new Counter();
console.log(counter.#count);
// SyntaxError
counter.#count = 42;
// SyntaxError
counter.count = 42;
// Tries to assign to the public field
Private class fields supported in Chrome 74, Node 12
34 / 48
Property access in JavaScript
// TypeError prone
const streetName = user.address.streetName;
// Pleasure to read
let streetName = user &&
user.address &&
user.address.streetName;
// Even better, ternaries
const streetName = user ? (
user.address ?
user.address.streetName : undefined
) : undefined;
35 / 48
Say ?. (Stage 3)
Enter optional chaining.
Optional chaining operator: ?.
Short circuits if encounters a nullish value.
Available as a Babel plugin, TypeScript 3.7
const streetName = user?.address?.streetName;
const street = user?.getAddress()?.streetName;
36 / 48
Assigning default values
const donut = incomingDonut ? incomingDonut : defaultDonut;
// alternative
const donut = incomingDonut || defaultDonut;
// except if this is a number, catch the zero's
// Or a boolean?!
const num = incomingNum == null ? defaultNum : incomingNum;
37 / 48
Nullish Coalescing Operator
const num = incomingNum ?? defaultNum;
Checks if a value is nullish.
In Kotlin, this is called the Elvis operator.
Available as a Babel plugin, TypeScript 3.7
38 / 48
Bringing it all together 💯
Soon, we could be writing code like this!
const streetName = user?.address?.streetName ?? 'No Street.';
39 / 48
Stage 4 (Finished)
Spec editor has signed off.
Ready to be included in the spec.
Test262 acceptance tests have been merged.
Two spec compliant implementations exist.
40 / 48
Numbers in JavaScript
Number can only represent values up to 253 - 1
const max = Number.MAX_SAFE_INTEGER;
// 9_007_199_254_740_991
max + 1;
// 9_007_199_254_740_992 ✅
max + 2;
// 9_007_199_254_740_992 ❌
(max + 1) === (max + 2);
// true 🤯
41 / 48
BigInt 🎉
A new primitive
Arbitrary precision integers
Works as expected with arithmetic operators
Does not work with Math functions
Chrome, Firefox, Node 12+
BigInt(Number.MAX_SAFE_INTEGER);
// 9_007_199_254_740_991n
BigInt(Number.MAX_SAFE_INTEGER) + 2n;
// 9_007_199_254_740_993n ✅
typeof 100;
// 'number'
typeof 100n;
// 'bigint'
100n + 100;
// TypeError
42 / 48
Promise.allSettled
Promise.all added in 2015
Promise.all short-circuits when one of the promises fails
Promise.allSettled returns a promise that resolves when each of
the promises has either been fulfilled or rejected.
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
const results = await Promise.allSettled(promises);
results.forEach(result => console.log(result.status));
// expected output:
// "fulfilled"
// "rejected"
43 / 48
globalThis
So many JavaScript environments and all the related global
objects.
// A naive attempt at getting the global `this`. Don’t use this!
const getGlobalThis = () => {
if (typeof globalThis !== 'undefined') return globalThis;
if (typeof self !== 'undefined') return self;
if (typeof window !== 'undefined') return window;
if (typeof global !== 'undefined') return global;
// Note: this might still return the wrong result!
if (typeof this !== 'undefined') return this;
throw new Error('Unable to locate global `this`');
};
const theGlobalThis = getGlobalThis();
Now we just have globalThis, yay portability!
Unified mechanism to access the global this in any
environment.
44 / 48
Recap ESNext
ECMAScript is evolved by the TC39 committee
A new version is released every year
Proposals can change till they reach Stage 4
Stage 3 proposals-
Class enhancements
Optional chaining
Nullish Coalescing
Stage 4 proposals-
BigInt
Promise.allSettled
globalThis
45 / 48
Conclusion
JavaScript has come a long way since its inception in 1995.
We are a passionate, diverse and vocal community.
As a member of this community, you can participate in its
evolution!
Always bet on JavaScript! 🚀
46 / 48
Sources
https://github.com/tc39/proposals
https://v8.dev/
https://github.com/tc39/ecma262
https://github.com/tc39/test262
https://test262.report/
https://2ality.com/2017/04/flatmap.html
https://tech-insider.org/java/research/1995/1204.html
47 / 48
Thank you!
Questions?
chayanika.khatua@ing.com

Whats new in ES2019

  • 1.
    What's new inES2019 Chayanika Khatua
  • 2.
    "Any application thatcan be written in JavaScript, will eventually by written in JavaScript." - Jeff Atwood, Co-founder of StackOverflow 2 / 48
  • 3.
    We are 10.7million developers worldwide. Together we support over 4.1 billion users of the web. 3 / 48
  • 4.
    Me, back inthe day Arrays for (var i = 0; i < students.length; i++) { newStudents.push(transform(students[i])); } Strings ...we write JavaScript very differently now. var message = name + ', You have $' + dollars + ' in your account. 4 / 48
  • 5.
  • 6.
    What we're talkingabout ECMAScript, a brief, not at all complete, history So, what is new in ES2019? How do proposals turn into features? ESNext proposals 6 / 48
  • 7.
    The year was1995 7 / 48
  • 8.
    Mocha is born BrendanEich creates Mocha in 10 days Java is really new and trendy. JavaScript is marketed as the lightweight cousin. Eventually renamed to JavaScript in December 1995 8 / 48
  • 9.
    JScript Microsoft releases JScriptin August 1996. 9 / 48
  • 10.
    ECMA Netscape approaches theEuropean Computer Manufacturers Association. JavaScript and JScript are implementations. The spec is called ECMAScript. ECMAScript 1 released in 1997. 10 / 48
  • 11.
  • 12.
    Array.prototype.flat Flattens an arrayaccording to the depth specified. const arr = [[1, 2], [3, 4, 5]]; const flattened = arr.flat(); // default depth is 1 // flattened = [1, 2, 3, 4, 5] Specifying the depth: const arr = [[1, 2], [[3]]]; const flattened = arr.flat(2); // flattened = [1, 2, 3] Arbitrary depth? Use Infinity. 12 / 48
  • 13.
    Use case const animals= await Promise.all([ getWildAnimals(), getWingedCreatures(), getPets(), ]); // animals = [[ 🦊 , 🦁 ], [ 🐥 , 🦢 ], [ 🐱 ]]; const flattenedAnimals = animals.flat(); // flattenedAnimals = [ 🦊 , 🦁 , 🐥 , 🦢 , 🐱 ] 13 / 48
  • 14.
    Array.prototype.flatMap With map() everyelement is translated to exactly one output element. With flatMap() every element can be transformed into a variable number of elements. 14 / 48
  • 15.
    Working with data //Folder = { files: [] } const folders = [folder1, folder2, ...]; const toFiles = folder => folder.files; // Alternative const result = folders.map(toFiles).flat(1); const result = folders.flatMap(toFiles); // result = [file1, file2, file3, ...]; // Before flatMap, not performant const result = folders.reduce( (acc, folder) => acc.concat(folder.files) , []); 15 / 48
  • 16.
    Mapping and filtering constnumbers = [ 1, 2, 3, 4 ]; const doubleEven = num => { if (num % 2 === 0) { return [ num * 2 ]; } return []; }; const filteredAndDoubled = numbers.flatMap(doubleEven); // filteredAndDoubled = [ 4, 8 ]; 16 / 48
  • 17.
  • 18.
    Object.fromEntries Counterpart to theObject.entries. const obj = Object.fromEntries([ ['a', 1], ['b', 2] ]); // obj = {a: 1, b: 2} console.log(Object.entries(obj)); // [ ['a', 1], ['b', 2] ] 18 / 48
  • 19.
    Object transformation const objectOG= { a: 1, b: 2, c: 3 }; const resultObject = Object.fromEntries( Object.entries(objectOG) .map(([ key, val ]) => [ key, val * 2 ]) ); // resultObject = { a: 2, b: 4, c: 6 } Alternative, pre 2019 const result = Object.keys(objectOG).reduce((acc, key) => { acc[key] = objectOG[key] * 2; return acc; }, {}); 19 / 48
  • 20.
    Maps and Objects constmap = new Map([ ['a', 1], ['b', 2] ]); const obj = Object.fromEntries(map); // obj = {a: 1, b: 2} 20 / 48
  • 21.
    Optional catch binding Pre2019 try { // Try something dangerous } catch (error) { logger.error(error); } Post 2019 The error parameter can be dropped. try { data = JSON.parse(response); } catch { data = defaultData; } 21 / 48
  • 22.
    Array.prototype.sort stable const arr= [ { name: 'C', value: 90 }, { name: 'A', value: 80 }, { name: 'B', value: 80 }, ]; // compareValueFunction compares the value property and should res arr.sort(compareValueFunction); // pre, possibly [ { name: 'B', value: 80 }, { name: 'A', value: 80 }, { name: 'C', value: 90 }, ]; // post, required [ { name: 'A', value: 80 }, { name: 'B', value: 80 }, { name: 'C', value: 90 }, ]; 22 / 48
  • 23.
    Recap ES2019 Array.prototype.{flat, flatMap} Object.fromEntries Optionalcatch binding Array.prototype.sort stable All these are supported in all modern browsers and Node 12+ 23 / 48
  • 24.
    The Evolution ofECMAScript
  • 25.
    The TC39 proposalprocess Technical committee 39 of ECMA International ECMA 262 specification Members are implementers that send delegates Committee meets six times a year A new process was defined after ES4 was abandoned, and ES6 was a massive release that took 6 years. 25 / 48
  • 26.
    Stage 0 (Strawperson) Formatto allow input into the specification Planned or presented to TC39 26 / 48
  • 27.
    Stage 1 (Proposal) Identifiedchampion or champions who will advance the addition. Describe the problem or requirement High level API Potential concerns have been identified 27 / 48
  • 28.
    Stage 2 (Draft) Initialspec text Incremental changes expected The committee expects the feature to be developed and eventually included in the standard 28 / 48
  • 29.
    Stage 3 (Candidate) Atthis stage, the proposal is considered complete No further work is possible without implementation experience Only changes considered critical based on implementation experience 29 / 48
  • 30.
    Classes right now Classeswere added in ES6 class Counter { constructor() { this.min = 0; this.max = 100; this._count = 0; } get count() { return this._count; } increment() { this._count++; } decrement() { this._count--; } } Counter.someStaticProp = 'Static'; 30 / 48
  • 31.
    As a User constcounter = new Counter(); console.log(counter.count); // 0 counter._count = 42; console.log(counter.count); // 42 31 / 48
  • 32.
    Class Fields (Stage3) class Counter { static someStaticProp = 'Static'; this.min = 0; this.max = 100; this._count = 0; get count() { return this._count; } increment() { this._count++; } decrement() { this._count--; } } Supported in Chrome 72, Node 12 32 / 48
  • 33.
    Privacy matters (Stage3) class Counter { #count = 0; get count() { return this.#count; } increment() { this.#count++; } decrement() { this.#count--; } } 33 / 48
  • 34.
    As a User constcounter = new Counter(); console.log(counter.#count); // SyntaxError counter.#count = 42; // SyntaxError counter.count = 42; // Tries to assign to the public field Private class fields supported in Chrome 74, Node 12 34 / 48
  • 35.
    Property access inJavaScript // TypeError prone const streetName = user.address.streetName; // Pleasure to read let streetName = user && user.address && user.address.streetName; // Even better, ternaries const streetName = user ? ( user.address ? user.address.streetName : undefined ) : undefined; 35 / 48
  • 36.
    Say ?. (Stage3) Enter optional chaining. Optional chaining operator: ?. Short circuits if encounters a nullish value. Available as a Babel plugin, TypeScript 3.7 const streetName = user?.address?.streetName; const street = user?.getAddress()?.streetName; 36 / 48
  • 37.
    Assigning default values constdonut = incomingDonut ? incomingDonut : defaultDonut; // alternative const donut = incomingDonut || defaultDonut; // except if this is a number, catch the zero's // Or a boolean?! const num = incomingNum == null ? defaultNum : incomingNum; 37 / 48
  • 38.
    Nullish Coalescing Operator constnum = incomingNum ?? defaultNum; Checks if a value is nullish. In Kotlin, this is called the Elvis operator. Available as a Babel plugin, TypeScript 3.7 38 / 48
  • 39.
    Bringing it alltogether 💯 Soon, we could be writing code like this! const streetName = user?.address?.streetName ?? 'No Street.'; 39 / 48
  • 40.
    Stage 4 (Finished) Speceditor has signed off. Ready to be included in the spec. Test262 acceptance tests have been merged. Two spec compliant implementations exist. 40 / 48
  • 41.
    Numbers in JavaScript Numbercan only represent values up to 253 - 1 const max = Number.MAX_SAFE_INTEGER; // 9_007_199_254_740_991 max + 1; // 9_007_199_254_740_992 ✅ max + 2; // 9_007_199_254_740_992 ❌ (max + 1) === (max + 2); // true 🤯 41 / 48
  • 42.
    BigInt 🎉 A newprimitive Arbitrary precision integers Works as expected with arithmetic operators Does not work with Math functions Chrome, Firefox, Node 12+ BigInt(Number.MAX_SAFE_INTEGER); // 9_007_199_254_740_991n BigInt(Number.MAX_SAFE_INTEGER) + 2n; // 9_007_199_254_740_993n ✅ typeof 100; // 'number' typeof 100n; // 'bigint' 100n + 100; // TypeError 42 / 48
  • 43.
    Promise.allSettled Promise.all added in2015 Promise.all short-circuits when one of the promises fails Promise.allSettled returns a promise that resolves when each of the promises has either been fulfilled or rejected. const promise1 = Promise.resolve(3); const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo')); const promises = [promise1, promise2]; const results = await Promise.allSettled(promises); results.forEach(result => console.log(result.status)); // expected output: // "fulfilled" // "rejected" 43 / 48
  • 44.
    globalThis So many JavaScriptenvironments and all the related global objects. // A naive attempt at getting the global `this`. Don’t use this! const getGlobalThis = () => { if (typeof globalThis !== 'undefined') return globalThis; if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; // Note: this might still return the wrong result! if (typeof this !== 'undefined') return this; throw new Error('Unable to locate global `this`'); }; const theGlobalThis = getGlobalThis(); Now we just have globalThis, yay portability! Unified mechanism to access the global this in any environment. 44 / 48
  • 45.
    Recap ESNext ECMAScript isevolved by the TC39 committee A new version is released every year Proposals can change till they reach Stage 4 Stage 3 proposals- Class enhancements Optional chaining Nullish Coalescing Stage 4 proposals- BigInt Promise.allSettled globalThis 45 / 48
  • 46.
    Conclusion JavaScript has comea long way since its inception in 1995. We are a passionate, diverse and vocal community. As a member of this community, you can participate in its evolution! Always bet on JavaScript! 🚀 46 / 48
  • 47.
  • 48.