Skip to content
This repository was archived by the owner on Sep 17, 2018. It is now read-only.
tav edited this page Mar 24, 2015 · 7 revisions

This fork of babel generates a more optimal output for today's browsers and is specialised for the µ micro-framework.

Default Parameters

Given a function with default parameters like:

function foo(a=2, b=3) {
  return a + b;
}

The upstream version generates output which uses the expensive arguments object:

function foo() {
  var a = arguments[0] === undefined ? 2 : arguments[0];
  var b = arguments[1] === undefined ? 3 : arguments[1];
  return a + b;
}

Whilst this is in line with the ES6 spec, the value it provides by making foo.length consistent is not something that's commonly used. Instead, we generate this more performant output:

function foo(a, b) {
  a === undefined && (a = 2);
  b === undefined && (b = 3);
  return a + b;
}

Export Declarations

When defining exports, e.g.

export function foo() {
  alert("hi");
}

The upstream version generates the equivalent of:

function foo() {
  alert("hi");
}

exports.foo = foo;
Object.defineProperty(exports, "__esModule", {
  value: true
});

Whilst defining __esModule helps with compatibility with various loaders, we don't use it within µ, so we instead just output:

function foo() {
  alert("hi");
}

exports.foo = foo;

Import Declarations

Similarly, when defining imports, e.g.

import foo from "foo";

Upstream will generate:

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };

var foo = _interopRequire(require("foo"));

We instead generate the following without the __esModule check:

var foo = require("foo")["default"];

Note: This only works for the default module type of common. If you specify an alternative type, e.g. babel --modules amd, then it will behave as it would in upstream.

For...Of Loops

Upstream currently transforms:

for (var user of users) {
  display(user.name);
}

Into:

var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
  for (var _iterator = users[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var user = _step.value;
    display(user.name);
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator["return"]) {
      _iterator["return"]();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}

Not only does this mean we can't use for...of loops on Arrays in older browsers without hacking Array.prototype, but it also puts a loop inside a try/catch which tends to be deadly for performance.

We instead generate the following:

for (var user, _isArray = Array.isArray(users), _i = 0, _j, _iterator = !_isArray && users[Symbol.iterator]();;) {
  if (_isArray) {
    if (_i >= users.length) break;
    user = users[_i++];
  } else {
    _j = _iterator.next();
    if (_j.done) break;
    user = _j.value;
  }
  display(user.name);
}

This fast-paths Array values in for...of loops like in --loose mode, drops support for calling any generator return values, and tries to minimise unnecessary assignments during variable declarations.

For...In Loops

In a complete break with standards, we desugar for...in loops to loop over Array values. That is, we transform loops like:

for (var user in users) {
  display(user.name);
}

Into:

for (var _i = 0; _i < users.length; _i++) {
  var user = users[_i];
  display(user.name);
}

The reasoning for this is that standard for...in loops were primarily used to find all the enumerable properties found directly on an object. Not only is Object.keys() a significantly faster way of finding that out, but it's also a lot shorter. Compare:

var props = Object.keys(obj);

Versus:

var hasProp = {}.hasOwnProperty,
    props = [];

for (var prop in obj) {
  if (!hasProp.call(obj, prop))
    continue;
  props.push(prop);
}

And since all the browsers we care about support Object.keys(), it made sense to take advantage of for...in loops as a way to loop over Arrays without having to constantly check if it's an Array on every iteration, like we do with for...of loops.

Array Comprehensions

Thanks to the non-standard, for...in loops, we can now do much faster comprehensions over Array values. For example, the following:

var cities = [for (user in users) if (user.isActive()) user.city];

Gets transpiled into:

var cities = (function () {
  var _cities = [];
  for (var _i = 0; _i < users.length; _i++) {
    var user = users[_i];
    if (user.isActive()) {
      _cities.push(user.city);
    }
  }
  return _cities;
})();

Note: var declarations automatically get injected into both for...in and for...of array and generator comprehensions, but not in loops.

JSX

Like React, we support the JSX syntax, e.g.

var Item = mu.View({
  render() {
    return <div class="item">{this.text}</div>;
  }
});

Gets translated into:

var Item = mu.View({
  _name: "Item",
  render: function render() {
    return mu.Tag("div", {className: "item"}, this.text);
  }
});

Minor differences to React are:

  • The _name property gets auto-assigned in place of the longer displayName.
  • String elements like "div" are instantiated using mu.Tag.
  • Non-string elements like Item are instantiated using mu.Elem.
  • Any use of literal class attributes in JSX get translated to className automatically.

Clone this wiki locally