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.]
Many plugins in Babel have two modes:
Normally, it is recommended to not use loose mode. The pros and cons are:
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",
···
],
···
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})`;
}
}
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.
"use strict";
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); // (A)
}
}
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;
})();
In loose mode, normal assignment is used to add methods (line A). This style is more like you’d hand-write code in ES5.
"use strict";
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() { // (A)
return "(" + this.x + ", " + this.y + ")";
};
return Point;
})();