ES6 & Traceur

Erik Arvidsson
GitHub: @arv
Twitter: @ErikArvidsson

Language Improvements to JavaScript

leads to:

Better code

Easier to maintain

Happier Developers!

Three topics on how to improve your code:

  1. ES6 features
    • Part of all browsers soon
  2. Your own language extensions
    • Make it part of your own tool chain
  3. Angular 2.0 language extensions
    • Part of the Angular tool chain

ECMAScript 6

  • What's new in ES6?
  • How will it make you happier?

Classes

Today


function Point3D(x, y, z) {
  Point.call(this, x, y);
  this.z = z;
}
Point3D.prototype = Object.create(
    Point.prototype);
Point3D.prototype.constructor = Point3D;
Point3D.prototype.equals = function(p2) {
  return Point.prototype.equals.call(this, p2)
      && this.z === p2.z;
};
          

Classes

ES6


class Point3D extends Point {
  constructor(x, y, z) {
    super(x, y);
    this.z = z;
  }
  equals(p2) {
    return super.equals(p2) && this.z === p2.z;
  }
}
 
 
          

Classes

  • When?
    • When using class like abstractions
  • What?
    • Prototype based. Just like you are used to!
    • Mostly syntactic sugar.
    • Working super!
    • Class side inheritance

Modules

Today

  • AMD
  • Common JS + Browserify

Modules

Today - AMD


define(['./Parser', './SourceFile'],
    function(Parser, SourceFile) {
      ...
      return exportedValue;
    });
          

Modules

Today - CJS


var Parser = require('./Parser');
var SourceFile = require('./SourceFile');
...
module.exports = exportedValue;
 
          

Modules

ES6


import {Parser} from './Parser.js';
import {SourceFile} from './SourceFile.js';
...
export class C { ... }
export function f() { ... }
          

Modules

  • When?
    • When your code does not fit in one file
    • For better logical grouping
  • What?
    • No synchronous load
    • Static dependencies!
    • Static validation
    • Cyclic dependencies

this scope

Today


class MenuButton {
  constructor(button) {
    button.onclick = function(e) {
      this.showMenu();
      // TypeError: undefined is not a function
    };
  }
  showMenu() { ... }
}
          

this scope

Today


class MenuButton {
  constructor(button) {
    button.onclick = function(e) {
      this.showMenu();
       
    }.bind(this);
  }
  showMenu() { ... }
}
          

Arrow Functions

ES6


class MenuButton {
  constructor(button) {
    button.onclick = e => this.showMenu();
     
     
     
  }
  showMenu() { ... }
}
          

Arrow Functions

  • When?
    • Any time you have bind(this)
    • Any time you have callbacks
  • What?
    • Short and sweet
    • Single expression or a body
    • Lexical this (as well as arguments and super)

Default parameters

Today


function saveFile(content, filename) {
  filename = filename || tempName();
  fs.writeFileSync(filename, content);
}
          

Default parameters

ES6


function saveFile(content,
                  filename = tempName()) {
  fs.writeFileSync(filename, content);
}
          

Default parameters

  • When?
    • When you have optional parameters
  • What?
    • Makes the intent clear
    • Handles falsey values correct

Rest parameters

Today


function max(x) {
  var args =
      Array.prototype.slice.call(arguments, 1);
  return args.reduce(
      (a, b) => a > b ? a : b, x);
}
          

Rest parameters

ES6


function max(x, ...args) {
   
   
  return args.reduce(
      (a, b) => a > b ? a : b, x);
}
          

Rest parameters

  • When?
    • When you use arguments
  • What?
    • Makes the intent clear
    • Is a real Array

And lots more...

So what?

"This is all good but when can I use this?"

Now!

Now!

Too much red?

Traceur to the rescue

What is Traceur?

  • Compiles ES6 (and more) to ES5
  • Works in all modern browsers!
  • A compiler written in ES6
  • Designed to be a platform for experimenting with new language features
  • Highly modular and extensible

How does Traceur work?

  • Compile
    • Compiles source code into an AST
  • Transform
    • Transforms the AST with the language extension to an AST without the language extension
    • One transformer per language extension
  • Write
    • Final AST is visited to output source and source maps

Your own language extension

Sample Transformer

Remove debug asserts


function assert(b) {
  if (!b) {
    throw new Error('Assertion failed');
  }
}
          

Sample Transformer

Remove debug asserts


// Transforms
assert(someExpression)

// To
void 0
          

Step 1 - Extend ParseTreeTransformer


class StripAssertTransformer
    extends ParseTreeTransformer {
  ...
}
          

Step 2 - Override transform method


transformCallExpression(tree) {
  var operand = tree.operand;
  if (operand.type === IDENTIFIER_EXPRESSION &&
      operand.getStringValue() === 'assert') {
    return createVoid0();
  }
  return super.transformCallExpression(tree);
}
          

Step 3 - Add transformer


// FromOptionsTransformer.js
if (!options.debug) {
  append(StripAssertTransformer);
}
          

Thats it!

Patch

Angular Language Extensions

AtScript

  • Makes programming Angular better through language extensions
    • Type annotations
    • Type assertions
    • Annotations

Type Annotations

Today


/**
 * @param {string} s
 * @param {number} n
 * @return {string}
 */
function repeat(s, n) {
  return Array(n + 1).join(s);
}
          

Type Annotations

AtScript


 
 
 
 
 
function repeat(s: string, n: number): string {
  return Array(n + 1).join(s);
}
          

Type Annotations

  • No more JSDoc comments
    • DRY - Don't repeat yourself
    • No out of bounds data - things do not get out of sync
    • Concise
  • Allows tools & IDEs to be awesome

Type Assertions

AtScript


function repeat(s: string, n: number): string {
  return Array(n + 1).join(s);
}
 
 
 
          

Type Assertions

AtScript


function repeat(s, n) {
  assert.argumentTypes(s, $tr.types.string,
                       n, $tr.types.number);
  return assert.returnType(
      (Array(n + 1).join(s)), $tr.types.string);
}
          

Type Assertions

  • Keeps you honest
  • Catches errors
    • Both in the code and in the type annotations
  • Debug mode

Annotations

Today


 
function repeat(s, n) {
  return Array(n + 1).join(s);
}
repeat.metadata = [...];
 
          

Annotations

AtScript


@Description('Repeats a string')
function repeat(s, n) {
  return Array(n + 1).join(s);
}
 
 
          

Annotations

AtScript


 
function repeat(s, n) {
  return Array(n + 1).join(s);
}
repeat.annotations = [
    new Description('Repeats a string')];
          

Annotations

  • Attaches meta data
  • Not out of bounds
  • Can later be used
    • Dependency injection
    • Component / Directive descriptions

Want to know more?