Harmony

in the Future of Javascript

Eric Bollens / eric@eb.io / ebollens [GitHub] / @ericbollens [Twitter]

Before We Start...

About This Presentation

The slides from this presentation
eb.io/p-js-harmony

This presentation is open source
github.com/ebollens/p-js-harmony

About Me

Chief Technology Officer

Formerly, Open Source Architect

Open source, modern web & IoT evangelist

A Brief History

Mocha

Java came to Netscape Navigator but wasn't enough

We aimed to provide a "glue language" for the Web designers and part time programmers who were building Web content from components such as images, plugins, and Java applets...where the glue programmers would assemble components and automate their interactions using [a scripting language].

And so Brendan Eich programmed it in ten days

Standardization

Two competing implementations
Mocha -> LiveScript -> JavaScript
JScript

ECMA International hosts the standard
ECMAScript (ECMA-262) begins Nov 1996
Technical Committee 39 (TC39)
ECMAScript 1 released June 1997

Early Days

Early progress was good
do-while, regex, string methods, exception handling, etc.
ECMAScript 3 released December 1999

But differentiation was built into the process
ECMAScript meant only to be the "core"
Each browser would package its own additional features

Growing Pains

a strange hybrid of powerful and universally supported core functionality and often incompatible object models

Navigator like IE, IE like Navigator, or abstract both?

And then throw some other junk in the mix...
DHTML
JS2 & JScript.net

HTML was also in the midst of its XHTML identity crisis

Fragmentation

A Massive Overhaul Proposed with ES4
Classes, Interfaces, Operator Overloading, Packages, Namespaces, Type Constraints, Verification, Type-dispatched Exceptions, Syntactic Sugar, Iteration Controls, Generators, Tail Calls, Parameterized Classes, Typed Literatls, Meta-level Hooks, Reflection, Destructing, Self-hosting, and more...

Opposition sparks a simpler & competing ES3.1

is it right for ES4 to be stalled in committee, or for ES3 to be forked into a now-hidden “ES3.1”, because Microsoft and Doug Crockford object to ES4 on general grounds?

Reunification with the Harmony Agenda

The split ends in Oslo in July 2008

A split committee is good for no one and nothing, least of all any language specs that might come out of it

Focus work on ES3.1 (eventually renamed ES5)
Syntactic extensions but more modest than ES4
Some ES4 proposals will be abandoned
Some ES4 goals rephrased to maintain consensus

Today and the Future

ES5 is well supported

ES6 finalized June 2015

ES7 work already underway

Ubiquity

AJAX Revolution

Node.js

JSON databases

On-device software

Compilation target

Language Features

Block Scoping

var a = 10;
if (a == 10) {
  var a = 1;
  console.log(a); // 1
} 
console.log(a); // 1
var a = 10;
if (a == 10) {
  let a = 1;
  console.log(a); // 1
} 
console.log(a); // 10
for (let i = 0; i<10; i++){
  console.log(i); // 0, 1, 2, 3, 4 ... 9
}

console.log(i); // i is not defined

Why Block Scoping?

for (var i = 0; i <= 5; i++) {
  items[i].onclick = function() {
    console.log("Item " + i + " is clicked.");
  };
}
for (var i = 0; i <= 5; i++) {
  (function(j){
    items[i].onclick = function (ev) {
      console.log("Item " + j + " is clicked.");
    };
  })(i);
}
for (var i = 0; i <= 5; i++) {
  let j = i;
  items[i].onclick = function (ev) {
    console.log("Item " + j + " is clicked.");
  };
}

Arrow Functions

Shorter functions

var a2 = a.map( function(s){ return s.length } );
var a3 = a.map( s => s.length );

Lexical this

var self = this;
setInterval(function(){
    self.age++;
}, 1000);
setInterval(() => {
    this.age++;
}, 1000);

Caveats with yield and returning object literals

Constants

Read-only reference to a value

const EX = 7;
(EX = 20) == 20;
EX == 7;
const EX = 10; 
// Uncaught TypeError: Identifier 'EX' has already been declared
var EX = 20;
EX == 7;

Objects as Constants

Reference is immutable, not value

const OBJ = { foo: 'bar' }
OBJ = null
console.log(OBJ); // Object {foo: "bar"}
OBJ.foo = 'baz';
console.log(OBJ); // Object {foo: "baz"}

Freeze allows for immutability of values

Object.freeze(OBJ);
OBJ.foo = 'qux';
console.log(OBJ); // Object {foo: "baz"}

Symbols

New primitive

typeof Symbol() == "symbol"

Every symbol has unique identity

Symbol('a') !== Symbol('a')

Function-defined, not constructor-based

new Symbol() // TypeError: Symbol is not a constructor

Computed Keys

const MY_KEY = Symbol();
let obj = {};
obj[MY_KEY] = 123;
console.log(obj[MY_KEY]); // 123
const MY_KEY = Symbol();
let obj = {
  [MY_KEY]: 123
};
console.log(obj[MY_KEY]); // 123
const MY_FN = Symbol();
let obj = {
    [MY_FN]() {
        return 'bar';
    }
};
obj[MY_FN]() == 'bar';

Enumerating Computed Keys

let obj = {
    [Symbol('my_key')]: 1,
    enum: 2,
    nonEnum: 3
};
Object.defineProperty(obj, 'nonEnum', { enumerable: false });

Object.getOwnPropertyNames(obj)  // ['enum', 'nonEnum']
Object.getOwnPropertySymbols(obj)  // [Symbol(my_key)]
Reflect.ownKeys(obj)  // [Symbol(my_key), 'enum', 'nonEnum']
Object.keys(obj)  // ['enum']

Symbol Registry

let sym = Symbol.for('foo')
let obj = {
    [sym]: 1
};
obj[Symbol.for('foo')] == 1
sym == Symbol.for('foo')
Symbol.keyFor(sym) == 'foo'

Yield

function* foo(){
  var i = 0;
  while(i < 3)
    yield i++;
}

var gen = foo();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // undefined

Yield*

function* foo(i) {
  let bound = i + 3;
  while(i < bound)
    yield i++;
}
function* bar(i){
  yield i;
  yield* foo(i);
  yield i + 10;
}
var gen = bar(10);

console.log(gen.next().value); // 10
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 20
console.log(gen.next().value); // undefined

Functions

Default Parameters

function multiply(a, b = 1) {
  return a*b;
}
multiply(5, 2); // 10
multiply(5); // 5
multiply(5, undefined); // 5
function append(value, array = []) {
  array.push(value);
  return array;
}

append(1); // [1]
append(2); // [2], not [1, 2]
function singularPlural(singular, plural = singular+"s"){ /* .. */ }

Rest Parameters

A real array of captured parameters

function sortArgs(...theArgs) {
  var sortedArgs = theArgs.sort();
  return sortedArgs;
}

console.log(sortRestArgs(5,3,7,1)); // shows 1,3,5,7

Only captures unnamed parameters

function multiply(multiplier, ...theArgs) {
  return theArgs.map(function (element) {
    return multiplier * element;
  });
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

Spread Operator

function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);
function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);

Also available to array literals

var partial = ['b', 'c'];
var full = ['a', ...partial, 'd', 'e'];

Object Orientation

Classes

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return '(' + this.x + ', ' + this.y + ')';
    }
}
var p = new Point(25, 8);
p.toString() == '(25, 8)';
typeof Point == 'function';
new Foo(); // ReferenceError
class Foo {} // because this is not hoisted

Constructor Methods

class Foo {
    constructor(prop) {
        this.prop = prop;
    }
}
Foo === Foo.prototype.constructor
typeof Foo == 'function'

Static Methods

class Foo {
    static staticMethod() {
        return 'classy';
    }
}
typeof Foo.staticMethod == 'function'
Foo.staticMethod() == 'classy'

Prototypical Methods

class Foo {
    prototypeMethod() {
        return 'prototypical';
    }
}
typeof Foo.prototype.prototypeMethod == 'function'
var bar = new Foo(prop);
bar.prototypeMethod() == 'prototypical'

Setters & Getters

class MyClass {
    get prop() {
        return 'getter';
    }
    set prop(value) {
        console.log('setter: '+value);
    }
}
inst.prop = 123;
// setter: 123
inst.prop == 'getter'

Inheritance

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() { return '(' + this.x + ', ' + this.y + ')'; }
}
class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y);
        this.color = color;
    }
    toString() { return super.toString() + ' in ' + this.color; }
}
var cp = new ColorPoint(25, 8, 'green');
cp.toString() == '(25, 8) in green'
cp instanceof ColorPoint
cp instanceof Point

Species

class MyArray1 extends Array { }

let result1 = new MyArray1().map(x => x);

console.log(result2 instanceof Array); // true
console.log(result1 instanceof MyArray1); // true
class MyArray2 extends Array {
    static get [Symbol.species]() {
        return Array;
    }
}

let result2 = new MyArray2().map(x => x);

console.log(result2 instanceof Array); // true
console.log(result2 instanceof MyArray2); // false

Modules

Named exports

export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return Math.sqrt(square(x) + square(y));
}
import { square, diag } from 'lib';
square(11); // 121
import * from 'lib';
lib.square(11); // 121

Modules

Default function export

export default function () { ... };
import myFunc from 'myFunc';
myFunc();

Default class export

export default class { ... };
import MyClass from 'MyClass';
let inst = new MyClass();

Modules

Mixing defaults and named exports

export default function (obj) {
    ...
};
export function each(obj, iterator, context) {
    ...
}
export { each as forEach };
import _, { each } from 'underscore';
import { default as _, each } from 'underscore';

Patterns & Features

Promises

myRequest.then(function(response){
    /* do something after request completes */
}).catch(function(errorText){
    /* handle error if request fails */
});
var myRequest = new Promise(function(resolve, reject){
    xhr.onload = function(){
        if(this.status == 200){
            resolve(this.response);
        }else{
            reject(this.statusText);
        }
    }
    xhr.onerror = function(){
        reject(this.statusText);
    }
    xhr.send();
});

Module Loaders

Merging promises and module loading

System.import('some_module').then(some_module => {
    // Use some_module
}).catch(error => {
    // ...
});
Promise.all(['module1', 'module2'].map(x => System.import(x)))
       .then(([module1, module2]) => {
           // Use module1 and module2
       });

Maps

var myMap = new Map();

No default keys from the prototype
Supports non-string keys
Determinable size
Specified insertion order

myMap.set('foo', 'bar');
myMap.get('foo') == 'bar';
var objKey = {};
myMap.set(objKey, 'baz');
myMap.get(objKey) == 'baz';
for(let key of myMap.keys())
for(let [key, value] of myMap.entries())
myMap.forEach(function(value, key){ /* .. */ })

Sets

var mySet = new Set();
mySet.add(1);
mySet.has(1) == true;
mySet.add('foo');
mySet.has('foo') == true;
var objVal = {};
mySet.add(objVal);
mySet.has(objVal) == true;
mySet.size == 3;
mySet.delete('foo');
for (let item of mySet)
mySet.forEach(function(value){ /* .. */ })
Array.from(mySet);

Template Strings

`string text`
`string text line 1
string text line 2`
`The number is ${a + b} and\nnot ${2 * a + b}.`
var a = 5;
var b = 10;

function myTag(strings, ...values) {
  console.log(strings[0]); // "Hello "
  console.log(strings[1]); // " world "
  console.log(values[0]);  // 15
  console.log(values[1]);  // 50
  return "Foobar!";
}

myTag`Hello ${ a + b } world ${ a * b}`; // Foobar!

Proxies

Define custom behavior for fundamental operations

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') 
      if (!Number.isInteger(value)) 
        throw new TypeError('The age is not an integer');
    obj[prop] = value;
  }
};
let person = new Proxy({}, validator);
person.age = 26;
person.age = 'young'; // Throws an exception

get, set, has, deleteProperty, defineProperty, apply, constructor, enumerate, ownKeys, getOwnPropertyDescriptor, getPrototypeOf, setPrototypeOf, isExtensible, preventExtensions

Beyond ES6

ES7

Early conceptualization includes...

Object.observe
SIMD
Async Functions
Typed Objects
Class Decorators
Class Properties
Bind Operator

asm.js

Strict subset of Javascript easily convertible to assembly
Only handles elementary numeric types
All external data in a single "heap" array
No globals, structures, closures, etc.

Commonly, source-to-source compilation
Emscripten and Mandreel
OpenGL, zlib, Unreal Engine 4, Unity, CPython, etc.

Averages 4x to 10x performance gains

WebAssembly (wasm)

Javascript is currently a compile target
asm.js makes it faster
Still high-level and interpreted

New low-level binary compile format
No object system or automatic garbage collection
Binary format, SIMD, threads, direct memory control

WebAssembly is an open invitation to developers building future programming languages.

Using the Future Today

Traceur

github.com/google/traceur-compiler

Good Resources

hacks.mozilla.org/category/es6-in-depth

2ality.com

es6-features.org

kangax.github.io/compat-table/es6

Thank You

Any Questions?

Eric Bollens / eric@eb.io / ebollens [GitHub] / @ericbollens [Twitter]