On Github mzgol / meet.js-summit-2013
Michał Gołębiowski
var x = 42;
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! } }
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; }
Leaking variables
var i = 42; for (var i = 0; i < 8; i++) {} console.log(i); // prints 8
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;
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]
IE11 & Chrome gotcha:
for (let i = 0; i < 3; i++) { setTimeout(function () { console.log(i); // prints 3 3 3, should print 0 1 2 }); }
Traceur by Google - transpiles ES6 to ES5
defs.js by Olov Lassus: just for let & const
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
The problem: the arguments object
function f(arg1) { g.apply(null, [].slice.call(arguments, 1)); }
Boilerplate code:
.apply(null, [].slice.call( ));
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"]
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 }
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
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]
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
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
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]
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"
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"
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]); } }
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)); } } };