Babelの手ほどき 前編 Babelとは
BabelはブラウザにまだサポートされていないようなJavaScriptの次世代の標準機能を、現在のブラウザでも使えるようにするNode.js製のトランスパイラです。Babelがどのような働きをするか解説します。
Babelとは
今回紹介するBabel(バベル)*は、次世代のJavaScriptの標準機能を、ブラウザのサポートを待たずに使えるようにするNode.js製のツールです。次世代の標準機能を使って書かれたコードを、それらの機能をサポートしていないブラウザでも動くコードに変換(トランスパイル)します。
「次世代のJavaScriptの標準機能」というのはECMAScript(以降、ESと呼びます)のバージョン6や7で追加された(る)機能、そしてさらにその先の標準機能です。
*注:元々は6to5という名前
Babelはもともと6to5という名前でした。6to5という名前では、ES6からES5への変換を行うツールだと勘違いされてしまうこと、ES7やその先の標準への対応も見据えていることなど、さまざまな理由からBabelという名前に変更されました。
ブラウザの次世代標準への対応状況
ECMAScript 6の新機能のシリーズでも紹介していますが、ES6には数多くの新機能や構文が追加されています。ES6のごく一部の機能をサポートしているブラウザは少しずつ増えていますが、包括的なサポートをしているブラウザはまだありません。便利な機能や構文が追加されても、ブラウザのサポートを待っていたら、いつまでたっても使うことができません。
ブラウザのサポートを待たない
Babelは、ES6やES7の機能や構文を、ES5相当の機能を使ってブラウザで実行できるように変換します。ES5は現在広く使われているブラウザのほとんどでサポートされています。ES5の機能であれば、現状でも問題なく使うことができます。
Babelを使えばブラウザのサポートを待たなくとも、次世代の標準機能や構文を使ってJavaScriptを書くことができます。
BabelとAltJS
Babelはコードを変換するという点では、CoffeeScriptやTypeScript*といった、JavaScriptのコードを生成するAltJSの仲間と言えるかもしれません。CoffeeScriptやTypeScriptでも、Class構文やArrow Functionといった、ES6に似た構文を使ってコードを書くことができます。構文を使うことはできますが、あくまでも書いているのはAltJSであって、本来のJavaScriptではありません。
*注:CoffeeScriptやTypeScript
AltJSと呼ばれるCoffeeScriptやTypeScriptに関しては、次のシリーズを参照してください。
- 「CoffeeScriptで学ぶJSの設計」シリーズ
- 「探検、TypeScript」シリーズ
Babelを使う際に書くのは、AltJSではなく純粋なJavaScriptです*。新しい機能や構文を使って書いたJavaScriptを、現状のブラウザでも動くJavaScriptへと変換します。
*注:AtlJSとJavaScript
ここで言いたいのは、BabelがAltJSではないということです。AltJSにはそれぞれ解決したい問題や利点があり、1つの手段として使用されています。単純な優劣を付けるのはナンセンスです。
JavaScriptで書くということ
JavaScriptで書く、ということには、いくつかの利点が考えられます。
ブラウザで実行する場合、現在のブラウザのサポート状況では変換が必要になります。しかしブラウザのサポートが進めば*、書いたコードは変換なしで実行できるようになるでしょう。
*注:ブラウザのサポートが進めば
ES6だけを見ても、多くのブラウザでサポートされるのは、まだまだ先の話でしょう。いつまでたってもサポートが進まずに、ずっと変換が必要かもしれません。先は読めませんが、希望を持つのは大事なことです。
学習コスト
Babelを使う上では、学習コストという言い方自体が似つかわしくないかもしれません。Babelを使う際に覚えるのは新しいJavaScriptの仕様です。フロントエンド・エンジニアが言語の仕様を覚えるのに「コスト」という言葉は相応しくないでしょう。AltJSやフレームワークには流行り廃りがありますが、言語の仕様は長く役に立つ知識*になります。
*注:言語の仕様は長く役に立つ知識
ただし仕様が草案段階などで、確定していない機能もあるので、それまでに使っていた構文が変更されることもあるかもしれません。
ブラウザ外でのサポート状況
ブラウザ外のサポート状況に触れておくと、Node.jsではES6の構文を実行できるオプションが用意されています。また、io.jsではデフォルトでいくつかの構文をサポートしていて、そのまま実行できます。ブラウザと比べて、サーバーサイドJavaScriptでは次世代標準への対応が早く進んでいます。
次世代標準を学ぶという意味でも、Babelを使うことにメリットがあると筆者は考えています。
Babelの機能
前置きが長くなりましたが、まずはBabelを使うと、どのような構文や機能が使えるのかを概観してみましょう。導入方法については後述します。
構文のサポート
Babelは、ES6で追加された新しい構文*のほぼすべてを使うことができます。数が多いので、すべては挙げませんが、たとえば次のようなものです。
- Class
- Template Strings
- Arrow Function
- Default Parameter
- Rest Parameter
- Property Literals
- Modules
*注:ES6の新機能
ES6の新機能に関しては、次のシリーズで詳しく解説していきます。
- 「ECMAScript 6の新機能」シリーズ
これらの構文を使って、次のようなJavaScriptを書くことができます。
class Base {
constructor(name) {
this.name = name;
}
showName() {
console.log(`my name is ${this.name}`);
}
}
class App extends Base {
constructor(name = 'nakajmg') {
super(name);
}
}
var app = new App();
app.showName();
Class構文を使ってBaseクラスを作り、extends
でBaseクラスを継承したAppクラスを作ります。Appクラスのconstructor
ではDefault Parameterを使い、引数がなかったときの初期値としてname
に"nakajmg"が代入されるようにしています。またsuper
というメソッドでBaseクラスのconstructor
を呼び出しています。
このコードと同じ動作をするものを、フレームワークやAltJSを使わないで書くとしたら、なかなか手が掛かると思います。Babelはこのコードを、現状で使われているブラウザで動くようにコードを変換してくれます。
上記のコードをBabelで変換すると、次のようなコードになります。
"use strict";
var _get = function get(object, property, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc && desc.writable) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; };
var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, static Props); return Constructor; }; })();
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
var Base = (function () {
function Base(name) {
_classCallCheck(this, Base);
this.name = name;
}
_createClass(Base, {
showName: {
value: function showName() {
console.log("my name is " + this.name);
}
}
});
return Base;
})();
var App = (function (_Base) {
function App() {
var name = arguments[0] === undefined ? "nakajmg" : arguments[0];
_classCallCheck(this, App);
_get(Object.getPrototypeOf(App.prototype), "constructor", this).call(this, name);
}
_inherits(App, _Base);
return App;
})(Base);
var app = new App();
app.showName();
Class構文を使った箇所は、通常のfunction
を使ったクラスっぽいものに変換されているのが確認できます。またコードの先頭の方には_get
や_inherits
など、元のコードには書いていないメソッドが追加されています。
これらはBabelが追加したコードで、extends
などの構文をES5の機能で実現するためのものです。ES6で追加された構文のほとんどはES5の機能で実装することができますが、自分で実装するには少し手がかかりますし、コードも読みづらくなります。その面倒な部分をBabelが補ってくれます。
Babelのインストールと実行
Babelはnpmでインストールします。コマンドラインから次のコマンドを実行してBabelをインストールします。
$ npm install -g babel
変換は次のような形式のコマンドで行います。
$ babel input.js --out-file output.js
--out-file
は出力するファイル名の指定をするオプションです。このオプションを指定しないと、実行してもコマンドライン上にコンパイル結果が表示されるだけで終わります。-o
と省略して指定することもできます。
*注:Babelの実行
今回はbabelコマンドを直接利用していますが、Gruntやgulpなどで利用することもできます。この方法についてはシリーズ後半で解説する予定です。
オプション
babelコマンドに指定できるオプションはいろいろなものがあります。おなじみの機能としては、ファイルの変更を監視してコンパイルを行う--watch
オプションなど、便利なオプションが用意されています。
blacklistオプション
babelコマンドでコンパイルを行うと、ES6のコードなどを変換してくれますが、変換しないでそのまま出力したい場合のために--blacklist
というオプションがあります。
Babelは変換を行う際にTransformerと呼ばれるモジュールのようなものを使って変換を行います。Transformerは一つ一つの機能に対応して存在しています。例えばArrow Functionであればes6.arrowFunctions
というTransformerを、Class構文であればes6.classes
というTransformerを利用します。
blacklistオプションに、これらのTransformerの名前を指定することで、変換を行わずに出力することが可能です。
たとえば、次のコードに対してblacklistオプションでes6.classes
を指定した場合を見てみましょう。
class App {
constructor() {
console.log('class syntax');
}
}
var func = () => {
console.log('arrow function');
};
上記のコードではES6のClass構文とArrow Functionを使っています。このコードに対して次のコマンドを実行します。
$ babel input.js --blacklist 'es6.classes' -o output.js
output.js
には次のような結果が出力されます。
"use strict";
class App {
constructor() {
console.log("class syntax");
}
}
var func = function () {
console.log("arrow function");
};
通常であれば変換されるClass構文が変換されずに、そのまま出力されています。Node.jsやio.jsといった特定の環境などで動かす場合には、blacklistオプションで変換の必要ないTransformerを指定する、といった最適化を行うとよいかもしれません。
whitelistオプション
blacklistオプションとは逆に、使用するTransformerを指定できるwhitelistオプションがあります。whitelistオプションを利用した場合、指定したTransformer以外は変換が行われずそのまま出力されます。
Transformerは数が多いので、Transformerの一覧は、次のドキュメントを参照してください。
そのほかのオプション
ほかにもいろいろなオプションがありますので、詳しくは次のドキュメントを参照するとよいと思います。
modulesオプションについては、次回詳しく解説します。
おわりに
今回はBabelの特徴や基本的な使い方について紹介しました。次回は新しいAPIを使うためのPolyfillの使い方や、さまざまな環境での利用方法について解説します。