What's the future of JavaScript?
By Francisco Ramos
Who am I?
Backgrounds
- Desktop apps using Visual Basic
- Backend dev using PHP (sometimes Python)
- I taught a little bit of Java
During my backend times I was always doing frontend: HTML + CSS + JS
I was a real Web Developer
... and I became Frontend
- It took me 3 years to really understand the beauty and power of JavaScript
- When that happened a light bulb went off in my head
- I became exclusively Frontend working as a UI developer
Since then, I've been doing only HTML + CSS + JavaScript
... but my passion is architectures, libraries, frameworks, code conventions, best practices...
I'm sort of a
JavaScript evangelist
What's so cool about JavaScript?
- JavaScript is really and purely an object oriented language:
- Everything is an object, even literals.
- It's a prototype-based language.
- No inheritance, but delegation.
- First-class function:
- Passing functions as arguments.
- Returning them as values.
- Assigning them to variables or data structures.
- Closures: the possibility for a function to remember its surroundings
A little bit of History
- 1995: JavaScript was born (LiveScript). Later on was standarized and ECMAScript was created
- 1997: ES1
- 1998: ES2
- 1999: ES3. RegExp, better string handling and Try Catch block
- 2000: ES4 started, but was abandoned in 2004
- 2011: ES5. JSON, Function.prototype.bind, lots of new methods in Array.prototype, Object.create, Object.defineProperty.
- 2012: ES6 (Harmony). Proposal freeze in 2013. Release 2014-2015
ES6 Harmony
new features and sugar
- Modules
- Proxies
- Promises
- => functions
- let: block scope
- Set, Map and WeakMap objects
- Generators: yield
- Array comprehensions
- Destructuring
- Enhanced object literals
- ...rest and ...spread operators
- for-of loops
- Default arguments
- Classes
modules
Feature for scalable applications
// Profile.js
export var firstName = 'Francisco';
export var lastName = 'Ramos';
export var year = 1978;
// ProfileView.js
import {firstName, lastName} from './Profile';
import {year as yearOfBirth} from './Profile';
function setHeader(element) {
element.textContent = firstName + ' ' + lastName;
}
Exports and Imports
// lib/math.js
let PI = 3.14159; // not exported
export function sum(a, b) { return a+b; }
export function square(x) { return x*x; }
export function area(r) { return PI*(r^2); }
// main.js
module math from 'lib/math';
console.log(math.square(3));
// main2.js
import * as math from 'lib/math';
console.log(math.area(5));
Importing the whole module
// main3.js
import 'lib/math' as math;
console.log(math.sume(2, 5));
// lib/utils.js
export const PI = 3.14159;
export default {
func1() {},
func2() {},
func3() {}
};
// app.js
import {PI} from 'lib/utils';
import utils from 'lib/utils';
console.log(PI);
utils.func1();
Default exports
Re-exporting things
// something.js
export * from 'Profile';
export {sum, square} from 'lib/math';
// main.js
import 'something' as stuff;
console.log(stuff.firstName);
console.log(stuff.lastName);
console.log(stuff.year);
stuff.sum(2, 2);
stuff.square(4);
... and more
- Inline modules
- Module script tag
- Programmatic loader API
- Circular references detection
- Async, non blocking loading
Proxies
Extending JavaScript
Meta-programming in JavaScript
let Person = {
name: 'Fran',
age: 35
};
let PersonWrap = Proxy(Person, Interceptor);
let Interceptor = {
get(target, prop, receiver) {}, // personWrap.name
set(target, prop, val, receiver) {}, // personWrap.age = 36
delete(target, prop) {}, // delete personWrap.name
has(target, prop) {}, // 'name' in personWrap
enumerate (target) {} // for (let prop in personWrap) {}
};
... and much more
{
getOwnPropertyDescriptor: function(target,name) {}, // -> Object.getOwnPropertyDescriptor(proxy,name)
getOwnPropertyNames: function(target) {}, // -> Object.getOwnPropertyNames(proxy)
getPrototypeOf: function(target) {}, // -> Object.getPrototypeOf(proxy)
defineProperty: function(target,name, desc) {}, // -> Object.defineProperty(proxy,name,desc)
deleteProperty: function(target,name) {}, // -> delete proxy[name]
freeze: function(target) {}, // -> Object.freeze(proxy)
seal: function(target) {}, // -> Object.seal(proxy)
preventExtensions: function(target) {}, // -> Object.preventExtensions(proxy)
isFrozen: function(target) {}, // -> Object.isFrozen(proxy)
isSealed: function(target) {}, // -> Object.isSealed(proxy)
isExtensible: function(target) {}, // -> Object.isExtensible(proxy)
hasOwn: function(target,name) {}, // -> ({}).hasOwnProperty.call(proxy,name)
keys: function(target) {}, // -> Object.keys(proxy)
apply: function(target,thisArg,args) {}, // -> proxy(...args)
construct: function(target,args) {} // -> new proxy(...args)
}
Must watch: Proxies are awesome by Brendan Eich
Promises
No more callbacks
Pyramid of doom :-(
(function () {
function init() {
fetchHtml('http://html.json', function (data) {
buildDOM(data, function () { // async??
fetchContent('http://content.json', function (data) {
bindContent(data, function () {
alert("I'm done!!");
})
});
});
});
}
init();
})();
Refactored version :-\
// hoisting callbacks
(function () {
function onContentBound() { alert("I'm done!!"); }
function onContentFetched(data) { bindContent(data, onContentBound); }
function onDOMBuilt() { fetchContent('http://content.json', onContentFetched); }
function onHtmlFetched(data) { buildDOM(data, onDOMBuilt); }
function init() { fetchHtml('http://html.json', onHtmlFetched); }
init();
})();
Using promises :-)
(function () {
init()
.then(fetchHtml)
.then(buildDOM)
.then(fetchContent)
.then(bindContent)
.then(() => alert("I'm done!!") )
.catch(() => alert("Something when wrong!!") )
;
})();
... and if the promise is resolved before registering the callback, once we do it, it's called immediately.
API
/* Constructor */
new Promise(function (resolve, reject) {});
/* Methods */
Promise.prototype.then(onFulfilled, onRejected)
Promise.prototype.catch(onRejected) == Promise.prototype.then(null, onRejected)
/* Static methods */
Promise.resolve(value) // -> resolved Promise
Promise.reject(reason) // -> rejected Promise
Promise.all(...promises) // -> Promise that will resolve when all promises resolved
=> functions
It's all about this
Arrows share the same lexical this as their surrounding code
var Form = {
init: function () {
this.$input = document.querySelector('input');
this.$button = document.querySelector('button');
this.attachEvents();
},
attachEvents: function () {
// ES3 - var that = this
var that = this; // scope variable for closure
document.addEventListener('click', this.$button, function (e) { that.onButtonClick(e) });
// ES5 - Function.prototype.bind
document.addEventListener('click', this.$button, this.onButtonClick.bind(this) );
// ES6 - arrow function
document.addEventListener('click', this.$button, e => this.onButtonClick(e) );
}
onButtonClick: function (e) { /* we do something when the use clicks on the button */ }
};
let: block scope
The new var
The problem
// ES3, ES5
var handlers = [];
for (var count = 0; count < 3; count++) {
handlers[count] = function () {
alert(count);
}
}
handlers[0](); // alerts "3"
handlers[1](); // alerts "3"
handlers[2](); // alerts "3"
// ES3, ES5
var handlers = [];
var count; // this variable is hoisted
for (count = 0; count < 3; count++) {
handlers[count] = function () {
alert(count);
}
}
handlers[0](); // alerts "3"
handlers[1](); // alerts "3"
Solution (a nasty one)
// ES3, ES5
var handlers = [];
for (var count = 0; count < 3; count++) {
(function (i) {
handlers[i] = function () {
alert(i)
};
})(count);
}
handlers[0](); // alerts "0"
handlers[1](); // alerts "1"
handlers[2](); // alerts "2"
// ES6
let handlers = [];
for (let count = 0; count < 3; count++) {
handlers[count] = function () {
alert(count);
}
}
handlers[0](); // alerts "0"
handlers[1](); // alerts "1"
... and much more
let x = 1; // let-definition
let(y = 2, z = 3) { // let-statement
// block where "x", "y" and "z" are visible
}
// scope where only "x" is visible
{ // new block scope
let i = 0; // will be visible within this block
}
let (
privVal = 5,
myPrivate = function () { /* do something private */ }
) {
var something = myPrivate();
function getVal() { return privVal; }
}
Set, Map and WeakMap objects
Handy tools to make our life easier
Set
Ordered list of values that cannot contain duplicates.
Nothing new if you come from Java or Phyton
let items = new Set();
items.add(5);
items.add("5");
items.add(5); // oops, duplicate - this is ignored
console.log(items.size); // 2
let items = new Set([1, 2, 3, 4, 5, 5, 5, 'Fran']);
console.log(items.size); // 6
consooe.log(items.has(2)); // true
items.delete('Fran');
consooe.log(items.size); // 5
Map
The basic idea is to map a value to a unique key,
any type of key, even objects
let myMap = new Map();
let key1 = 'bla';
let key2 = document.getElementById("myDiv");
myMap.set(key1, 'value1');
myMap.set(key2, metadata);
// later
let value = myMap.get('bla');
let meta = myMap.get(document.getElementById("myDiv"));
WeakMap
Same as Map objects, but the key must be an object.
There is a reason for that...
let wMap = new WeakMap();
let element = document.querySelector(".element");
wMap.set(element, "My Value");
console.log(wMap.get(element)); // "My Value"
// later - remove reference
element.parentNode.removeChild(element);
element = null; // the element gets garbage collected
console.log(wMap.size); // 0
A WeakMap holds only a weak reference to a key
... more efficient memory consumption :-)
generators: yield
co-routines/multi-task
... and what can I do with this ???
function *myGenerator() {
// some code here
yield myTask1();
yield myTask2();
yield myTask3();
// more code here
}
let g = myGenerator();
g.next(); // executes myTask1 returning result
g.next(); // executes myTask2 returning result
g.next(); // executes myTask3 returning result
More new syntax
- Iterate over infinite streams
var fibonacci = function* () {
let [prev, curr] = [0, 1];
for (;;) { // infinite iteration
[prev, curr] = [curr, prev + curr];
yield curr;
}
};
for (let val of fibonacci()) {
if (val > 1000) break;
console.log(val); // 1, 2, 3, 5, 8...
}
var fib = fibonacci();
console.log(fib.next()); // 1
console.log(fib.next()); // 2
console.log(fib.next()); // 3
console.log(fib.next()); // 5
console.log(fib.next()); // 8
- We can create custom iterators
function* keyValue(obj) {
for (let k in obj) {
yield [k, obj[k]];
}
}
let Person = {
name: 'Fran',
age: 36,
country: 'Spain'
}
for (let [key, val] of keyValue(Person)) { ... }
- Asynchronous programming
let userTasks = (function* () {
// Coroutines that return promises
let a = yield $.ajax('http://get/user');
let b = yield $.ajax('http://process/user', a);
yield $.ajax('http://save/user', b);
})();
userTasks.next() // gets the user
.then( user => {
// we do something with the user and then...
return userTasks.next(user); // processes the user
})
.then( user => {
// we do something else after the user has been processed and then...
return userTasks.next(user); // saves the user
})
.then( user => {
// final operations here
})
;
Elegant way of array processing
array comprehensions
// ES6
let arr = [1, 2, 3, 4, 5];
//1. Array filter
let mult2 = [for (x of arr) if (x % 2 == 0) x];
console.log(mult2); // [2, 4]
//2. Array map
let pow2 = [for (x of arr) Math.pow(x, 2)];
console.log(pow2); // [1, 4, 9, 16, 25]
// ES5
var arr = [1, 2, 3, 4, 5];
//1. Array filter
var mult2 = arr.filter(function (val, i) { return val % 2 == 0; });
console.log(mult2); // [2, 4]
//2. Array map
var pow2 = arr.map(function (val, i) { return Math.pow(val, 2); });
console.log(pow2); // [1, 4, 9, 16, 25]
Picked up from languages such as Python or CoffeeScript
destructuring
Saving you lots of typing
Destructuring arrays
let [x, y] = [10, -10, 5];
console.log(x, y); // 10, -10;
let [a, , b] = [1, 2, 3];
console.log(a, b); // 1, 3;
// variables swapping
[a, b] = [b, a];
Destructuring objects
let user = {
name: 'Fran',
age: 36,
location: [15, -20]
};
let {name: n, age: a, location: l} = user;
console.log(n, a, l[0], l[1]); // Fran, 36, 15, -20
// or even shorter
let {name, age, location: [lat, lng]} = user;
console.log(name, age, lat, lng); // Fran, 36, 15, -20
Destructuring of function arguments
// ES3, ES5
function ajax (config) {
var url = config.url;
var data = config.data || {};
var method = config.method || 'get';
var complete = config.complete || new Function;
// this is too "noisy" :-/
}
ajax({
url: 'http://www.whatever.com/method',
data: { ... },
method: 'post',
complete: function () { ... }
});
// ES6
function ajax ({ url, data, method, complete }) {
console.log(url, data, method, complete);
}
enhanced object literals
Even more typing saving
// ES3, ES5
function getTypeId() { return 'passport'; };
var Person = {
name: 'Fran',
getTypeId: getTypeId,
sayName: function () {
alert('My name is ' + this.name);
}
};
Person[getTypeId()] = '12341234x';
Too much typing. We need something simpler...
// ES6
function getTypeId() { return 'passport'; };
var Person = {
name: 'Fran',
getTypeId, // shorthand for getTypeId: getTypeId
sayName() { alert('My name is ' + this.name); },
[getTypeId()] = '12341234x' // computed property names
};
Object-based design
var vehicle = {
color: '',
speed: 0,
accelerate(up) { this.speed += up; },
slowDown(down) { this.speed -= down; }
};
var Ferrari = {
__proto__: vehicle,
color: 'red',
accelerate(up) { super.accelerate(up + 30); }
};
var plane = {
__proto__: vehicle,
color: 'white',
altitude: 0,
takeOff(up) {
super.accelerate(300);
this.altitude += up;
},
fly() { this.takeOff(10000); }
};
...rest and ...spread operators
Fixing "misdesign"
// ES3, ES5
function foo (x, y) {
var rest = [].slice.call(arguments, 2);
// (arguments instanceof Array) === false
// now "rest" is a real array
rest.slice( ... );
rest.map( ... );
}
// ES6
function foo (x, y, ...rest) {
// "rest" is a real array!!
for (let i of rest) { ... }
rest.slice( ... );
rest.filter( ... );
rest.map( ... );
}
It gets weirder though
// ES3, ES5
function MyType() {
var args = [].slice.call(arguments);
this.processArgs.apply(this, args); // I mean, WTF!? O_O
}
MyType.prototype.processArgs = function (arg1, arg2) {
var rest = [].slice.call(arguments, 2);
};
// ES6
function MyType (...args) {
this.processArgs(...args);
}
MyType.prototype.processArgs = function (arg1, arg2, ...rest) {
// "rest" is an array
}
Wait, it gets better...
function storeUserDetails(name, age, country) {
$.ajax('http://www.example.com/storeUser', {...});
}
let userInfo = ['Fran', 36, 'Spain'];
// ES5
storeUserDetails.apply(null, userInfo);
// ES6
storeUserDetails(...userInfo);
for-of loops
Complementing for-in
TO-DO
default parameters
Function params values by default
TO-DO
classes
Let's make everybody happy
This is what a class will look like
class MyType { // new keyword "class"
// "strict mode";
private privAttr; // let privAttr = Symbol();
static someProp = true; // MyType.someProp = true;
constructor(arg1, arg2) { // function MyType() {}
public pubAttr = arg1; // this.pubAttr = arg1;
this.privAttr = arg1 * arg2;
}
public pubMethod() { // MyType.prototype.pubMethod = function () {}
return [MyType.someProp, this.privAttr];
}
}
Let's have a look at the inheritance
class MySubType extends MyType { // MySubType.prototype = Object.create(MyType.prototype);
constructor(arg1, arg2) {
super(arg1); // MyType.call(this, arg1)
}
pubMethod (arg1) {
super.pubMethod(arg1); // MyType.prototype.pubMethod.call(this, arg1)
}
get x() {} // Object.defineProperty(this, 'x', { get: function() {} })
set y() {} // Object.defineProperty(this, 'y', { set: function() {} })
}
... and much more coming up
- Binary data
- String interpolation
- Standard modules
- Deferred functions
- Symbols
- Unicode support
- Object.observe (ES7)
- Number additions
- String additions
- Math additions
- RegExp additions
- etc...
play aroung
learn more
- ES6 features by Luke Hoban
- ES6 learning by Eric Douglas
- ES6 lessons by egghead.io
- Count to 6 by Domenic Denicola
- es6rocks.com
- ES6 Promises by Kake Archibald
- Basics of ES6 Generators by Kyle Simpson
- Proxies are awesome by Brendan Eich
- Workflows for ES6 Modules by Guy Bedford
- ES6 Sets by Nicholas C. Zakas
- ES6 Maps by Nicholas C. Zakas
- ES6 WeakMaps by Nicholas C. Zakas
- ECMAScript 6 LinkedIn group by me ;-)
- etc... just google ;-)
that's all folks!
What's the future of JavaScript
By Francisco Ramos
What's the future of JavaScript
- 1,362