Babel.js and ES6/7 Syntax
July 17th, 2015
Topics
- JavaScript ( <= ES5 )
- New Syntax in ES6/7
- Helpful Workflow Tools
JavaScript Features (ES5 and worse)
- callback hell
- verbose prototypical Inheritance
- dynamic scoping
- clunky ancient Array Iteration
- no (baked in) module system
// highway to callback hell
var that = this;
d3.getData(function(x){
     d3.getMoreData(x, function(y){
        d3.getEvenMoreData(y, function(z){ 
            that.process(x, y, z);
        });
    });
});
// what is this, Java!?
EnterpriseGrade.prototype.getStrategyCounters = function(arr) {
    // what is this, C!?
    for (var i = 0, getNums = []; i < arr.length; i++) {
        getNums[i] = function() {
            return arr[i]; // hope this works...
        };
    }
}
System.import('main.js');
define(['foo'], function (foo) {
    var baz = require('baz');
    return function () { foo.doSomething(baz); };
});New Syntax
improvements to ECMA-262
- 
ECMAScript 7 Proposal (various levels)
	- Stage 0 - Strawman (3 in babel)
- Stage 1 - Proposal (5)
- Stage 2 - Draft (1)
- Stage 3 - Candidate (none yet)
- Stage 4 - Finished (none yet)
 
Features
- 
	Block Scoping 
- 
	Arrow Functions 
- 
	Better Object Literals 
- 
	Destructuring 
- 
	Template Strings 
- 
	Function Parameter Improvements 
- 
	Symbols 
- 
	Iterators 
- 
	Generators 
- 
	Classes 
- 
	Modules 
ES6
- 
	Comprehensions 
- 
	Asynchronous Functions 
- 
	Decorators 
- 
	New Operators 
ES7
Babel.js
Do Not Dispair!
Subsequent JavaScript editions are
Strict Supersets
of their predecessors
ES6 Features
Source
Transpiled ES5
Block scoping (ES6)
const a = 1;
let b = 2;
if (a) {
    let b = 1;
    console.log(b);
    let x = 1;
}
console.log(x) // not defined
a = 2; // static error (immutable value)
let b = 1; // static error (re-declaration)
"use strict";
var a = 1;
var b = 2;
if (a) {
    var _b = 1;
    console.log(_b);
    var _x = 1;
}
console.log(x);Block Scoping (ES6)
Takeaways
- 
	no more reason to use var!
- 
	for (let = ...)
- no redeclaration!
- no hoisting!
- 
constprovides immutable references
- babel is smart
Source
Transpiled ES5
Better Object Literals (ES6)
let obj = {
    // Shorthand for ‘handler: handler’
    handler,
    
    // Methods
    toString() {
     // Super calls
     return "d " + super.toString();
    },
    
    // Computed (dynamic) property names
    [ "prop_" + (() => 42)() ]: 42
};
"use strict";
var _obj;
var _get = function get(_x, _x2, _x3) {...
function _defineProperty(obj, key, value) {...
var obj = _obj = _defineProperty({
    // Shorthand for ‘handler: handler’
    handler: handler,
    // Methods
    toString: function toString() {
        // Super calls
        return "d " + _get(
            Object.getPrototypeOf(_obj), 
            "toString", 
            this
        ).call(this);
    }
}, "prop_" + (function () {
    return 42;
})(), 42);
// Computed (dynamic) property namesBetter Object Literals (ES6)
Takeaways
- 
	{prop}
- 
	{method() { super.method() }}
- {[a + b] : 'c'}
- { ['me' + 'thod']() { } }
Source
Transpiled ES5
Arrow Functions (ES6)
let bob = {
  _name: "Bob",
  nameGetter() {
    return f => this._name;
  }
}
let f = x => x + 1;
let fn = (x, y, z) => { return x + y + z; };
let noop = ()=>{};
let curry = x => y => x + y;
// won't work! must wrap object in parens...
let literal = x => {a : 1};
var bob = {
  _name: "Bob",
  nameGetter: function nameGetter() {
    var _this = this;
    return function (f) {
      return _this._name;
    };
  }
};
var f = function f(x) {
  return x + 1;
};
var fn = function fn(x, y, z) {
  return x + y + z;
};
var noop = function noop() {};
var curry = function curry(x) {
  return function (y) {
    return x + y;
  };
};
Arrow Functions (ES6)
Takeaways
- 
	lexical this! 
- babel does it the same way we do (var this = _this)
- 
	parens + braces matter depending on the context
Source
Transpiled ES5
Destructuring (ES6)
let arr = [1, 2, 3, 4, 5];
let w = {
  x : 1, 
  y : { z : [2, 3] }
};
// unpack arrays, with defaults
let [a=2, b, ...rest] = arr;
// unpack object properties, at any level
let {x} = w;
// get fancy
let {
    y : {
        z: [p, q]
    }, 
    y
} = w;    "use strict";
var _slicedToArray = (function () { ...
var arr = [1, 2, 3, 4, 5];
var w = {
  x: 1,
  y: { z: [2, 3] }
};
// unpack arrays, with defaults
var _arr$0 = arr[0];
var a = _arr$0 === undefined ? 2 : _arr$0;
var b = arr[1];
var rest = arr.slice(2);
// unpack object properties, at any level
var x = w.x;
// get fancy
var _w$y$z = _slicedToArray(w.y.z, 2);
var p = _w$y$z[0];
var q = _w$y$z[1];
var y = w.y;Destructuring (ES6)
Takeaways
- 
	var prop = obj.prop, x = obj.p[0] .....
- 
	let {prop, p :[x, y]} = obj;
- 
	Much DRY-er extraction of variables 
- 
	extends to function parameters! 
Source
Transpiled ES5
Template Strings (ES6)
app.directive('Test', function() {
  return {
    template : `
      <div class="{{'classy'}}">
        Text
      </div>
    `
  }
})
let imputed = `http://${domain}/?${params}`
let v1 = 1, v2 = 2;
// no parens!
console.log `a${v1}b${v2}c`
// => ["a","b","c"] 1 2'use strict';
function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
app.directive('Test', function () {
  return {
    template: '\n      <div class="{{\'classy\'}}">\n        Text\n      </div>\n    '
  };
});
var imputed = 'http://' + domain + '/?' + params;
var v1 = 1,
    v2 = 2;
console.log(
  _taggedTemplateLiteral(
    ['a', 'b', 'c'], ['a', 'b', 'c']
  ), v1, v2
);
// => ["a","b","c"] 1 2Template Strings (ES6)
Takeaways
- 
	Multiline strings including whitespace! 
- 
	Nested quotes (of multiple types) 
- 
	Easy inline HTML 
- 
	Variable Interpolation 
- 
	String.raw
Source
Transpiled ES5
function f(a=true, ...rest) {
  return a ? rest.join(", ") : rest;
}
let g = ({x, y}) => {
  x += 1; z += 1;
  return {x, y};
}
let h = (a, b, c) => a + b + c;
let a = [1, 2, 3];
console.log( h(...a) )"use strict";
function f() {
  var a = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
  for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    rest[_key - 1] = arguments[_key];
  }
  return a ? rest.join(", ") : rest;
}
function g(_ref) {
  var x = _ref.x;
  var y = _ref.y;
  x += 1;z += 1;
  return { x: x, y: y };
}
var h = function h(a, b, c) {
  return a + b + c;
}
var a = [1, 2, 3];
console.log(h.apply(undefined, a));Function Parameter Improvements (ES6)
Takeaways
- 
	...args > Array.prototype.slice.call(arguments) 
- 
	arr.push(...args) > arr.push.apply(arr, args) 
- 
	Default parameters 
- Destructuring
- (Almost) as nifty as Python **kwargs
Source
Transpiled ES5
Symbols (ES6)
var MyClass = (function() {
  // module scoped symbol
  var key = Symbol("key");
  function MyClass(privateData) {
    this[key] = privateData;
  }
  MyClass.prototype = {
    doStuff: function() {
      return this[key];
    }
  };
  return MyClass;
})();
// new symbol
var key = Symbol("key");
var c = new MyClass("hello")
console.log(c[key] === undefined)"use strict";
var MyClass = (function () {
  // module scoped symbol
  var key = Symbol("key");
  function MyClass(privateData) {
    this[key] = privateData;
  }
  MyClass.prototype = {
    doStuff: function doStuff() {
      return this[key];
    }
  };
  return MyClass;
})();
// new symbol
var key = Symbol("key");
var c = new MyClass("hello");
console.log(c[key] === undefined);Symbols (ES6)
Takeaways
- 
	(Essentially) Private methods / attributes, useful for collision prevention 
- Special Symbols (Symbol.iterator)
Source
Transpiled ES5
Iterators (ES6)
let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        return { done: false, value: cur }
      }
    }
  }
}"use strict";
function _defineProperty(obj, key, value) {...
var fibonacci = _defineProperty(
    {}, 
    Symbol.iterator, 
    function () {
      var pre = 0,
          cur = 1;
      return {
        next: function next() {
          var _ref = [cur, pre + cur];
          pre = _ref[0];
          cur = _ref[1];
    
          return { done: false, value: cur };
        }
      };
    }
);Iterators (ES6)
Takeaways
- 
	Iterators are any object which has a method next -> {done : <bool>, value : <any>}nextwhich is defined as...
- 
	Objects with a [ Symbol.iterator] method are iterable (Array, Set, Map, arguments, String)
Source
Transpiled ES5
Iterators (continued) (ES6)
for (let n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
  for (var _iterator = fibonacci[Symbol.iterator](), _step; 
       !(_iteratorNormalCompletion = (_step = _iterator.next()).done); 
       _iteratorNormalCompletion = true) {
    var n = _step.value;
    // truncate the sequence at 1000
    if (n > 1000) break;
    console.log(n);
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator["return"]) {
      _iterator["return"]();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}Iterators (ES6)
Takeaways
- 
	for (let item of iterable)...
- performance concerns? (new object on each step)
Source
Transpiled ES5
Generators (ES6)
function* naturalNumbers() {
  let n = 1;
  while (true) yield n++; 
}
for (let n of naturalNumbers()) {
  if (n > 20) {
    break;
  }
  console.log(n);
}var marked0$0 = [naturalNumbers].map(regeneratorRuntime.mark);
function naturalNumbers() {
  var n;
  return regeneratorRuntime.wrap(function naturalNumbers$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        n = 1;
      case 1:
        if (!true) {
          context$1$0.next = 6;
          break;
        }
        context$1$0.next = 4;
        return n++;
      case 4:
        context$1$0.next = 1;
        break;
      case 6:
      case "end":
        return context$1$0.stop();
    }
  }, marked0$0[0], this);
}Generators (ES6)
Takeaways
- 
	Generators return Iterables 
- 
	Babel slices the function context between the yield statements and produces an FSM 
Source
Transpiled ES5
Classes (ES6)
class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);
    this.idMatrix = (
      SkinnedMesh.defaultMat()
    );
  }
  update(camera) {
    super.update();
  }
  static defaultMat() {
    return new THREE.Matrix4();
  }
}var _createClass = (function () {...
var _get = function get(_x, _x2, _x3) {...
function _classCallCheck(instance, Constructor) {...
function _inherits(subClass, superClass) {...
var SkinnedMesh = (function (_THREE$Mesh) {
  _inherits(SkinnedMesh, _THREE$Mesh);
  function SkinnedMesh(geometry, materials) {
    _classCallCheck(this, SkinnedMesh);
    _get(Object.getPrototypeOf(SkinnedMesh.prototype), "constructor", this).call(this, geometry, materials);
    this.idMatrix = SkinnedMesh.defaultMat();
  }
  _createClass(SkinnedMesh, [{
    key: "update",
    value: function update(camera) {
      _get(Object.getPrototypeOf(SkinnedMesh.prototype), "update", this).call(this);
    }
  }], [{
    key: "defaultMat",
    value: function defaultMat() {
      return new THREE.Matrix4();
    }
  }]);
  return SkinnedMesh;
})(THREE.Mesh);Classes (ES6)
Takeaways
- 
	Classes are just sugar (concise and convenient sugar) 
- Easy access to prototype's methods and constructor
Source
Transpiled ES5
Modules (ES6)
import _ from 'lodash';
import {a, b, c} from './mycode';
_.map(a, b);
export const one = 1;
export default function main() {}exports.__esModule = true;
var _temporalUndefined = {};
var one = _temporalUndefined;
exports['default'] = main;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _temporalAssertDefined(val, name, undef) { if (val === undef) { throw new ReferenceError(name + ' is not defined - temporal dead zone'); } return true; }
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _mycode = require('./mycode');
_lodash2['default'].map(_mycode.a, _mycode.b);
exports.one = one = 1;
exports.one = _temporalAssertDefined(one, 'one', _temporalUndefined) && one;
function main() {}Modules (ES6)
Takeaways
- 
	Named / default exports 
- destructuring in imports
- Babel / CommonJs interop
ES7 Features
Source
Transpiled ES5
Comprehensions (array) (ES7)
let evens = [
  for (n of [1,2,3,4,5,6])
    if (n%2 === 0)
     n
]"use strict";
var evens = (function () {
  var _evens = [];
  var _arr = [1, 2, 3, 4, 5, 6];
  for (var _i = 0; _i < _arr.length; _i++) {
    var n = _arr[_i];
    if (n % 2 === 0) {
      _evens.push(n);
    }
  }
  return _evens;
})();Source
Transpiled ES5
Comprehensions (generator) (ES7)
let evens = ( // <- note parens
  for (n of [1,2,3,4,5,6])
    if (n%2 === 0)
     n
)var _this = this;
var evens = regeneratorRuntime.mark(function callee$0$0() {
  var _arr, _i, n;
  return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        _arr = [1, 2, 3, 4, 5, 6];
        _i = 0;
      case 2:
        if (!(_i < _arr.length)) {
          context$1$0.next = 10;
          break;
        }
        n = _arr[_i];
        if (!(n % 2 === 0)) {
          context$1$0.next = 7;
          break;
        }
        context$1$0.next = 7;
        return n;
      case 7:
        _i++;
        context$1$0.next = 2;
        break;
      case 10:
      case "end":
        return context$1$0.stop();
    }
  }, callee$0$0, _this);
})();Comprehensions (ES7)
Takeaways
- 
	only for ... ofandifare allowed
- 
	(...) -> generator, [...] -> array 
- 
	need double parens if doing a generator comprehension in a function call -> fn(( for (c of ...) ... ))
- Multiple for or if statements ok
- Iterator performance concerns
let evens = (
  for (n of [1,2,3,4,5,6])
    for (j of [1,2,3])
      for (z of [5,6,7])
        if (j > 6)
          n 
)Source
Transpiled ES5
Async Functions (ES7)
async function get() {
  let val = await Promise.resolve(1);
  console.log(val);
  return val;
}
get().then(v => console.log(v + 1));
let getter = async() => { /*...*/ }"use strict";
function get() {
  var val;
  return regeneratorRuntime.async(function get$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        context$1$0.next = 2;
        return regeneratorRuntime.awrap(Promise.resolve(1));
      case 2:
        val = context$1$0.sent;
        console.log(val);
        return context$1$0.abrupt("return", val);
      case 5:
      case "end":
        return context$1$0.stop();
    }
  }, null, this);
}
get().then(function (v) {
  return console.log(v + 1);
});Async Functions (ES7)
Takeaways
- 
	Async functions return Promises 
- 
	Babel executes them via a Iterator FSM, calling nexton resolution ofawaitstatements
- 
	await* [...] === await Promise.all([....]) 
- 
	Try / Catch works like synchronous code 
- Issues with Angular 1.0 $q (see https://github.com/angular/angular.js/issues/6697)
Source
Transpiled ES5
Decorators (ES7)
@annotationFactory(true)
@nameThis
class MyClass { }
let nameThis = target => {
    target.named = 'target';
};
let annotationFactory = value => target => {
  target.annotated = value;
};
'use strict';
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var MyClass = (function () {
  function MyClass() {
    _classCallCheck(this, _MyClass);
  }
  var _MyClass = MyClass;
  MyClass = nameThis(MyClass) || MyClass;
  MyClass = annotationFactory(true)(MyClass) || MyClass;
  return MyClass;
})();
var nameThis = function nameThis(target) {
  return target.named = 'target';
};
var annotationFactory = function annotationFactory(value) {
  return function (target) {
    target.annotated = value;
  };
};Decorators (ES7)
Takeaways
- 
	Simply functions which wrap a class upon definition 
- can be function factories
- Execution order is bottom up
Bonus ES7
Features in Babel
- 
	::console.log === console.log.bind(console) 
- 
	obj::method()::other() === other.call(method.call(object)) 
- Map, Set, WeakMap, WeakSet
- 
	a**b === Math.pow(a, b) 
Babel Workflow
- node-source-map-support : package which can display babel generated source maps in node error stack
- 
	babel-eslint : Use Babel for linting of new syntax 
- ES6 Syntax highlighting : (babel-sublime, vim + atom have built in support)
- Custom syntax transformation : https://babeljs.io/docs/advanced/plugins/
Final Thoughts
- Performance and Size
- http://babeljs.io/docs/advanced/loose/
- http://babeljs.io/docs/usage/runtime/
New JavaScript Syntax
By Ben Southgate
New JavaScript Syntax
A walkthrough of syntax changes in ES6 + ES7 made available now through Babel.js
- 1,180
 
   
   
  