Future of JavaScript: ECMAScript Harmony – meet.js summit, 19.10.2013 – History of JavaScript



Future of JavaScript: ECMAScript Harmony – meet.js summit, 19.10.2013 – History of JavaScript

1 1


meet.js-summit-2013

Slides from my various talks

On Github mzgol / meet.js-summit-2013

Future of JavaScript: ECMAScript Harmony

meet.js summit, 19.10.2013

Michał Gołębiowski

About me

History of JavaScript

  • Created in 10 days of May 1995 by Brendan Eich.
  • Submitted to the Ecma International, where it was standardized as ECMAScript.
  • ECMAScript 1, 2, 3, ...4? Abandoned
  • Double-tiered approach:
  • "Fix" the language in ECMAScript 5.1.
    • strict mode: more errors, no implicit globals
    • minor syntax additions (getters, setters)
  • New syntax in ECMAScript 6, codename Harmony.
  • Simultaneous work on ECMAScript 7.

The var keyword

var x = 42;
  • function scope
  • hoisting
function f() {
    if (false) {
        var x = 42;
    } else {
        x; // no error, `x` is already declared!
    }
}

What's actually going on.

function f() {
    var x;
    if (false) {
        x = 42;
    } else {
        x; // no error, `x` is already declared!
    }
}

The var keyword

Function scope:

if (x == null) {
    var x = 42;
}

Let's change the fallback a little...

if (x == null) { // ReferenceError
    var y = 42;
}

What's actually executed:

var x;
if (x == null) { // `x` is declared before that line
    x = 42;
}
var y;
if (x == null) { // ReferenceError
    y = 42;
}

The var keyword

Function scope:

Leaking variables

var i = 42;
for (var i = 0; i < 8; i++) {}
console.log(i); // prints 8

The let keyword

Block scope

if (true) {
    let x = 42;
}
console.log(x); // ReferenceError
let x = 42;
if (true) {
    let x = 8;
    console.log(x); // prints 8
}
console.log(x); // prints 42

No hoisting

x = 2; // ReferenceError
let x;

The const keyword

  • Rules analogous to let:
    • Block scope
    • No hoisting
  • Requires initialization
  • Can't be overwritten

The const keyword

Requires initialization

const x; // SyntaxError

Can't be overwritten

const x = 42;
x = 8; // error!

const is shallow!

const a = [1, 2];
a[0] = "a";
console.log(a); // prints ["a", 2]
Olov Lassus: const is the new var, not let!

let, const: browser support

  • Best support: Internet Explorer 11
  • Chrome: under the flag and only in strict mode
  • Firefox: not conforming to the spec (but let was their idea).

IE11 & Chrome gotcha:

for (let i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(i); // prints 3 3 3, should print 0 1 2
    });
}

let, const: can you use it?

Traceur by Google - transpiles ES6 to ES5

defs.js by Olov Lassus: just for let & const

defs.js

Non-intrusive: preserves whitespaces

let x = 42;
if (true) {
    let x = 8;
}

defs output

var x = 42;
if (true) {
    var x$0 = 8;
}

No source maps (yet?) but they're not essential

grunt-defs by me: http://github.com/EE/grunt-defs

Rest parameters, spread operator

The problem: the arguments object

function f(arg1) {
    g.apply(null, [].slice.call(arguments, 1));
}

Boilerplate code:

     .apply(null, [].slice.call(            ));
                    

Rest parameters, spread operator

Rest parameters

function f(arg1, ...rest) {
    console.log(rest); // rest is a regular array!
}
f(2, "a", 3); // prints ["a", 3]

Spread operator

const a = ["a", 2];
f(...a); // equivalent to f("a", 2)
const b = [0, ...a, "c"]; // `b` is [0, "a", 2, "c"]

Rest parameters, spread operator

The problem: the arguments object

function f(arg1) {
    g.apply(null, [].slice.call(arguments, 1));
}

Rewritten:

function f(arg1, ...rest) { // rest parameters
    g(...rest); // spread operator
}

Rest parameters, spread operator

Another example: f(a, b, c) === g(0, 0, b, c, 1)

function f(arg1) {
    var args = [].slice.call(arguments, 1);
    args.unshift(0, 0);
    args.push(1);
    g.apply(null, args);
}

Rewritten:

function f(arg1, ...rest) {
    g(0, 0, ...rest, 1);
}

Browser support: Firefox

Arrow functions

function keyword is too long!

Why do we have to use return for a single-line function?

[1, 2, 3, 4].map(function (x) {return x * x;}); // -> [1, 4, 9, 16]

Boilerplate code:

                 function ( ) {return      ;})
                    

Let's shorten it

[1, 2, 3, 4].map(x => x * x); // -> [1, 4, 9, 16]

Arrow functions

Non-lexical this

function EnsureF() {
    this.invoke = function (arg) {
        f(arg);
    };
    setTimeout(function () {
        console.error("this.invoke wasn't called!");
        this.invoke(0); // oopsie!
    }, 10000);
}
function EnsureF() {
    this.invoke = arg => {
        f(arg);
    };
    setTimeout(() => {
        console.error("this.invoke wasn't called!");
        this.invoke(0); // works fine!
    }, 10000);
}

Browser support: Firefox

for-of

Iterating over arrays, sets, etc.

var a = ["a", "b", "c"];
for (var e in a) {
    console.log(e); // prints 0 1 2
}
var a = ["a", "b", "c"];
for (var i = 1; i < a.length; i++) {
    console.log(a[i]); // prints a b c
}

In ES6:

var a = ["a", "b", "c"];
for (let e of a) {
    console.log(e); // prints a b c
}

Browser support: Firefox

Default parameter values

function f(par1, par2, par3) {
    if (typeof par1 === "undefined") {
        par1 = 6;
    }
    if (typeof par1 === "undefined") {
        par2 = par1 + 1;
    }
    if (typeof par1 === "undefined") {
        par3 = par1 * par2;
    }
    console.log(par1, par2, par3);
}
function f(par1 = 6, par2 = par1 + 1, par3 = par1 * par2) {
    console.log([par1, par2, par3]);
}
f();        // prints [6, 7, 42]
f(1);       // prints [1, 2, 2]
f(1, 1);    // prints [1, 1, 1]
f(1, 1, 2); // prints [1, 1, 2]

Classes

Simulating classical inheritance

function Person(name) {
    this.name = name;
}
Person.prototype.introduce = function () {
    console.log("I'm " + this.name);
};
function Programmer(name) {
    Person.call(this, name);
}
Programmer.prototype = Object.create(Person.prototype);
Programmer.prototype.constructor = Programmer;
Programmer.prototype.introduce = function () {
    Person.prototype.introduce.apply(this, arguments);
    console.log("I'm a programmer");
};
var programmer = new Programmer("John");
programmer.introduce(); // prints "I'm John", "I'm a programmer"

Classes

Simulating classical inheritance

class Person {
    constructor(name) {
        this.name = name;
    }
    introduce() {
        console.log("I'm " + this.name);
    }
}
class Programmer extends Person {
    introduce() {
        super();
        console.log("I'm a programmer");
    }
}
var programmer = new Programmer("John");
programmer.introduce(); // prints "I'm John", "I'm a programmer"

Let's mix it up!

function Superhero(name) {
    if (typef name === "undefined") { name = "Superhero"; }
    this.name = name;
    this.powers = [].slice.call(arguments, 1);
    this.powers.unshift("speed");
}
Superhero.prototype.usePower = function (power) {
    console.log("I've used the power: " + power);
}
Superhero.prototype.schedulePowers = function () {
    var self = this;
    for (var i = 0; i < this.powers.length; i++) {
        (function (power) {
            setTimeout(function () { self.usePower(power); });
        })(this.powers[i]);
    }
}

Let's mix it up!

class Superhero {
    constructor(name = "Superhero", ...powers) {
        this.name = name;
        this.powers = ["speed", ...powers];
    }
    usePower(power) {
        console.log("I've used the power: " + power);
    }
    schedulePowers() {
        for (let power of this.powers) {
            setTimeout(() => this.usePower(power));
        }
    }
};

Traceur: jsbin.com

Thank you!