Eric Bollens / eric@eb.io / ebollens [GitHub] / @ericbollens [Twitter]
The slides from this presentation
eb.io/p-js-harmony
This presentation is open source
github.com/ebollens/p-js-harmony
Chief Technology Officer
Formerly,
Open Source Architect
Open source, modern web & IoT evangelist
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
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 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
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
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?
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
ES5 is well supported
ES6 finalized June 2015
ES7 work already underway
AJAX Revolution
Node.js
JSON databases
On-device software
Compilation target
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
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.");
};
}
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
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;
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"}
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
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';
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']
let sym = Symbol.for('foo')
let obj = {
[sym]: 1
};
obj[Symbol.for('foo')] == 1
sym == Symbol.for('foo')
Symbol.keyFor(sym) == 'foo'
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
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
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"){ /* .. */ }
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]
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'];
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
class Foo {
constructor(prop) {
this.prop = prop;
}
}
Foo === Foo.prototype.constructor
typeof Foo == 'function'
class Foo {
static staticMethod() {
return 'classy';
}
}
typeof Foo.staticMethod == 'function'
Foo.staticMethod() == 'classy'
class Foo {
prototypeMethod() {
return 'prototypical';
}
}
typeof Foo.prototype.prototypeMethod == 'function'
var bar = new Foo(prop);
bar.prototypeMethod() == 'prototypical'
class MyClass {
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
inst.prop = 123;
// setter: 123
inst.prop == 'getter'
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
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
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
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();
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';
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();
});
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
});
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){ /* .. */ })
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);
`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!
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
Early conceptualization includes...
Object.observe
SIMD
Async Functions
Typed Objects
Class Decorators
Class Properties
Bind Operator
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
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.
Eric Bollens / eric@eb.io / ebollens [GitHub] / @ericbollens [Twitter]