Babel’s loose mode transpiles ES6 code to ES5 code that is less faithful to ES6 semantics. This blog post explains how that works and what the pros and cons are (spoiler: normally not recommended).

Starting point for this series of posts on Babel 6:Configuring Babel 6” [explains the basics: configuration files, presets, plugins, etc.]

Two modes #

Many plugins in Babel have two modes:

Normally, it is recommended to not use loose mode. The pros and cons are:

Switching on loose mode #

The preset es2015-loose is the loose version of the standard ES6 preset, es2015. The preset’s code provides a good overview of what plugins have a loose mode and how to switch it on. This is an excerpt:

module.exports = { plugins: [ ··· [require("babel-plugin-transform-es2015-classes"), {loose: true}], require("babel-plugin-transform-es2015-object-super"), ··· ] };

This is a CommonJS module where you can use all of ECMAScript 5. If you configure Babel via .babelrc or package.json (details), you need to use JSON. You can either include the whole preset:

··· "presets": [ ··· "es2015-loose", ··· ], ···

Or you can include plugins individually:

··· "plugins": [ ··· ["transform-es2015-classes", {loose: true}], "transform-es2015-object-super", ··· ], ···

Example: the output of normal mode and loose mode #

Let’s see how the modes affect the transpilation of the following code.

class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return `(${this.x}, ${this.y})`; } }

Normal mode #

In normal mode, the prototype methods of the class are added via Object.defineProperty (line A), to ensure that they are non-enumerable, as required by the ES6 spec.

; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Point = (function () { function Point(x, y) { _classCallCheck(this, Point); this.x = x; this.y = y; } _createClass(Point, [{ key: "toString", value: function toString() { return "(" + this.x + ", " + this.y + ")"; } }]); return Point; })();

Loose mode #

In loose mode, normal assignment is used to add methods (line A). This style is more like you’d hand-write code in ES5.

; function _classCallCheck(instance, Constructor) { ··· } var Point = (function () { function Point(x, y) { _classCallCheck(this, Point); this.x = x; this.y = y; } Point.prototype.toString = function toString() { return "(" + this.x + ", " + this.y + ")"; }; return Point; })();

Originally published at 2ality.com.