Ecmascript2015と
その周辺について
2016-11-19(土) 第6回okayama-js
株式会社リゾーム システム開発部 尾古 豊明
自己紹介
名前:尾古 豊明
所属:株式会社リゾーム
TwitterID:@patorash
界隈ではパトさんと呼ばれています
理学部の生物科出身。ネットワークエンジニアしてたけど
会社都合により25歳でPHPerに強制転向。
オレオレフレームワーク、CakePHP、WordPressなどを経てAndroidやって、1年個
人事業主やって、チーム開発がしたくなりリゾームに入社し、RubyとRailsを覚える。
JSはPHPプログラマ3ヶ月目から短納期・ドキュメントのないオレオレフーレムワーク
のプロジェクトに突っ込まれ、Prototype.js、script.aculo.usを経験して使えるようにな
りました。
会社の話
とは?
ショッピングセンター(SC)・専門店向けのシステム開発やコンサ
ルティング業務をしている会社です。
Windows系・Java・Rubyなどで自社製品の
開発を行っています。
● 戦略会議NEXT
● BOND GATE(コミュニケーションウェア)
● MallNavi
● 交渉管理ware
● SC GATE(SC・ショップDB)
SC GATEとは?
SCと、そのテナント(ショップ)と運営元の企業を横断的に検索・
比較できるデータベースシステムです。
● SCの出店計画を立てるため
● 専門店の出店計画
● ライバルの動向チェック
などなど。
SC GATEを支える技術
● プログラミング言語 :Ruby 2.3.1
● フレームワーク   :Rails 4.2.6
● プラットフォーム  :Heroku
● データベース    :Postgresql
● ソースコード管理  :github
● CSSフレームワーク :Bootstrap
● AltJS :CoffeeScript
● CSS :Sass
● テンプレートエンジン :Slim
● JSライブラリ :jQuery,Knockout
最近の業務と、思ってたこと
● Railsをやっているため、CoffeeScriptばかり書いていて、JS
の進化に置いていかれがち
● 個人的にNode.jsもほとんど触ったことがない。npmもgrunt
もgulpもわからない。
● イケてるJSやろうと思ったら大変そうという印象
● とりあえず飛び込んでみよう!という名の
発表駆動勉強です
今日話すこと
1. Ecmascriptについて
2. Babelって何?
3. Browserifyって何?
4. Gulpって何?
5. Webpackって何?
今日話さないこと
1. 僕の考えた最強のビルドシステムとか
(そこまでやってないから)
2. テストランナーとか
(やってない)
3. gruntとgulpの違いとか
(gruntやってない)
4. es2015の使い心地
a. 実務で使ってないので詳しくはわからない
ES2015とは?
● 標準化団体のEcma Internationalによって標準化された言
語仕様。
● 各ブラウザで動作するJSはこの言語仕様を元に実装され
る。
● もう既にES2016もある。
● 対応状況は以下のURLで確認できる。
● http://kangax.github.io/compat-table/es6/
● es3,es5,es6とナンバリングされていたけれど、年表示に変
わることになった。es6=es2015。
ES2015の特徴
1. クラス定義(他の言語のような)
a. JSはプロトタイプベースのオブジェクト指向言語で他の言語のようなクラス
定義ができなかった
2. import, exportによるコードのモジュール化
3. 関数構文の改善(アロー関数、可変長引数、引数のデフォ
ルト値)
4. let, constによるブロックスコープ変数
5. for ...of命令
6. Promise、コレクション、Proxy
export default class Person {
constructor(name="taro", age=20) { // デフォルト引数
this.name = name;
this.age = age;
}
static version() {
return "1.0.0";
}
show() {
console.log(`${ this.name }さん(${ this.age }歳)`); // 変数展開
}
testLet() {
if (true) {
let a = "let"; // aの変数スコープはこの if文の中だけ
console.log(a);
a = "change"; // 再代入はできる
console.log(a);
}
console.log(a); // ReferenceError
}
testConst() {
if (true) {
const b = "const"; // bの変数スコープはこの if文の中だけ
console.log(b);
// b = "error"; // SyntaxError 定数なので再代入不可能
}
console.log(b); // ReferenceError
}
testArrowFunction() {
const arr = [1,2,3,4,5];
arr.forEach((v) => { // アロー関数
console.log(this);
console.log(v);
})
}
}
export default class Person {
constructor(name="taro", age=20) { // デフォルト引数
this.name = name;
this.age = age;
}
static version() {
return "1.0.0";
}
// ...省略
}
1. exportとclass定義
2. デフォルト引数
3. staticメソッド
export default class Person {
// 略
testLet() {
if (true) {
let a = "let"; // aの変数スコープはこの if文の中だけ
console.log(a);
a = "change"; // 再代入はできる
console.log(a);
}
console.log(a); // ReferenceError
}
testConst() {
if (true) {
const b = "const"; // bの変数スコープはこの if文の中だけ
console.log(b);
// b = "error"; // SyntaxError 定数なので再代入不可能
}
console.log(b); // ReferenceError
}
// 略
}
1. letでの変数宣言
スコープをブロック内に制限
再代入可能
2. constでの変数宣言
スコープをブロック内に制限
再代入不可
export default class Person {
//
testArrowFunction() {
const arr = [1,2,3,4,5];
arr.forEach((v) => { // アロー関数
console.log(this); // Person { name: "taro", age: 20 }
console.log(v);
})
}
}
1. アロー関数
thisのスコープが親スコープのままな
ので、let self = this; が不要
2. 今まで通りのスコープにしたければ、
今まで通り function で
定義する
'use strict';
import Person from './person'
document.addEventListener("DOMContentLoaded", (event) => {
console.log(Person.version());
const person = new Person();
person.show();
person.testArrowFunction();
});
1. importでPersonクラスを読み込む
2. staticメソッドを読んでみる
3. 変数personは再代入することはない
のでconstで定義
ES2015はすぐ使えるのか?
1. 最近のブラウザしかサポートしていない
2. ブラウザのサポート状況もまちまち(ES5まではサポートして
いるものが多い)
3. 現実的には、実戦投入は難しい
4. しかし今後、各ブラウザが対応した後にes5で書いたものを
es2016に変換していくのは辛い
5. 今からes2015を使いたい!
そこでトランスパイルですよ
トランスパイルとは?
1. プログラミング言語を他の言語に変換すること
2. AltJS(CoffeeScriptなど)もトランスパイルの一種
3. ES2015+ => ES5に変換することが多い
4. それをやるのがBabel
1. ES2015以上 => ES5に変換するトランスパイラー
2. Node.jsで作られている
3. IE9以上であればだいたい動くコードができる
4. .babelrcにオプション設定を記述することができる
ここでBabelを使ったサンプルコード
'use strict';
var _person = require('./person');
var _person2 = _interopRequireDefault(_person);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
document.addEventListener("DOMContentLoaded", function (event) {
console.log(_person2.default.version());
var person = new _person2.default();
person.show();
person.testArrowFunction();
});
'use strict';
import Person from './person'
document.addEventListener("DOMContentLoaded", (event) => {
console.log(Person.version());
const person = new Person();
person.show();
person.testArrowFunction();
});
Babel
_人人人人人人_
> 動かない <
 ̄Y^Y^Y^Y^Y ̄
1. import文を書いているところがBabelによって
require文に変換される。
2. ブラウザはrequireでのファイル読込をサポートしていない
(サーバサイドJSならOK)
3. require文があったらそれを解釈してファイルの内容を取り
込む処理が必要
4. それをやるのがBrowserify
Babelだけでは動かない…
1. Node.jsで使えるrequire構文をブラウザでも使えるようにするもの
2. 参照先のファイルを結合して1つのファイルにしてくれる
3. BabelはES2015のexport,importを書いたらrequireに変換するだけで、ファイル
を結合しない!
4. そのため、Babelで変換後、browserifyで結合する必要がある
5. なので、babelとbrowserifyはセットで使われるケースがほとんど
ここでBabelを使ったサンプルコード
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var
l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
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 Person = function () {
function Person() {
var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "taro";
var age = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 20;
_classCallCheck(this, Person);
// デフォルト引数
this.name = name;
this.age = age;
}
_createClass(Person, [{
key: "show",
value: function show() {
console.log(this.name + "u3055u3093(" + this.age + "u6B73)"); // 変数展開
'use strict';
var _person = require('./person');
var _person2 = _interopRequireDefault(_person);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
document.addEventListener("DOMContentLoaded", function (event) {
console.log(_person2.default.version());
var person = new _person2.default();
person.show();
person.testArrowFunction();
});
Browserify
_人人人人人_
> 動いた <
 ̄Y^Y^Y^Y ̄
BabelとBrowserifyはセット
1. Babel実行後、Browserifyを実行すると、使えるES5にな
る。
2. しかしBabelを実行してからBrowserifyを実行するの、単純
に面倒臭い
3. セットなんだから1回でやってほしい
4. それをやるのがBabelify
Babelify
1. browserifyを使う前にbabelを使ってくれる
2. browserify app.js -o bundle.js -t babelify
ちなみに
1. babelはファイルを監視して再コンパイルする機能がある(-w
オプション)
2. babel -w es2015/app.js -o js/app.js
3. browserifyにはそういうオプションがない…
4. それをやるのがWatchify
watchify
1. watch mode for browerify(公式githubより引用)
2. つまり、差分を検知して差分のみbrowserifyでコンパイルし
てくれる。高速!
3. -vオプションでコンパイル時間を出力してくれる
4. watchifyはbrowserifyのラッパーライブラリなので、引数は
browserifyのものが使える
5. watchify app.js -o bundle.js -t babelify
おさらい
babel…
ES2015=>ES5へのトランスパイラ。
import文をrequireに変換する。
browserify…
requireを読んでファイルに連結するやつ
babelify…
bebel実行後、browserifyを実行するbrowserifyの拡張
watchify…
ファイルの変更を検知して、
都度、browserifyを実行してくれるやつ
css、sassはどーするの?
1. JavaScriptの自動コンパイルはできそうだが、CSSはどー
するの?Sass使いたいですし!
2. それをやるのがnode-sass
node-sass
1. rubyのsassじゃなくてCで実装されたlibsassを使った高速な
コンパイルが特徴。node-sassはそのラッパー
2. -wオプションでファイル監視ができる
3. --output-styleオプションで出力形式も設定可能
4. node-sass -w --source-map true --output-style
compressed scss/style.scss css/style.css
ここまでの感想
1. watchifyとnode-sassを同時実行しておけば自動コンパイル
してくれる。やりたいことできた…。
2. しかしターミナル2つ立ち上げて2つ同時実行しておかないと
いけないの面倒。コマンド1つでどっちも面倒見てほしい。
3. そうか!だからgulpを使うのか!?
ちょっと休憩
そしてgulpに入門
gulpとは?
1. Node.jsのstream APIを使ったタスクランナー
2. ワークフローの自動化と高速化
3. やりたいことをタスク単位で定義
4. デフォルトタスクを定義しておくと、
タスク名を省略できる
sassタスクを定義
1. gulp-sassでsassのコンパイルを定義
2. gulp-autoprefixerでcssのprefixを自動定義
3. gulp-sourcemapsでsource-mapを生成
const gulp = require("gulp");
const sass = require("gulp-sass");
const autoprefixer = require("gulp-autoprefixer");
const sourcemaps = require("gulp-sourcemaps");
gulp.task("sass", () => {
"use strict";
return gulp.src("app/scss/**/*.scss")
.pipe(sourcemaps.init())
.pipe(sass({
outputStyle: 'compressed'
}).on('error', sass.logError))
.pipe(autoprefixer())
.pipe(sourcemaps.write("maps"))
.pipe(gulp.dest("app/dest/css"));
});
watchifyタスクを定義
1. babel, browserify, babelify, watchifyを利用
2. gulpで扱える形式にするために、vinyl-source-streamを利用する
3. updateイベントを検知したら再コンパイルする
const gulp = require("gulp");
const browserify = require("browserify");
const watchify = require("watchify");
const babelify = require("babelify");
const source = require("vinyl-source-stream");
const uglify = require("gulp-uglify");
gulp.task("watchify", () => {
"use strict";
return jscompile(true);
});
function jscompile(watch) {
"use strict";
let bundler = browserify();
if (watch) {
bundler = watchify(bundler)
}
bundler.add('app/js/app.js');
function rebuild() {
return bundler.transform(babelify)
.bundle()
.pipe(source("app.js"))
.pipe(gulp.dest("app/dest/js/"));
}
bundler.on("update", () => {
return rebuild();
});
bundler.on("log", (message) => {
console.log(message);
});
return rebuild();
}
defaultタスクを定義
● sass, watchifyタスクを同時に呼び出す
● gulpと打つだけでes2015,sassが使える!!
● 楽しい!!!
gulp.task("default", ["sass", "watchify"], () => {
"use strict";
gulp.watch("app/scss/**/*.scss", ["sass"]);
});
ここまでの感想
1. よっしゃーこれでgulpって書くだけでes2015,sass使えるよう
になったでー。
2. それにしても長い道のりやで…。これ理解しながらやってい
くことができるの、プログラマくらいちゃうか?
3. ちょろっとsassやjsを書きたいデザイナーさん達には
激しく辛い気がするんだが。
4. ん?Webpack???
Webpack
WebPack
1. 文字通り、Webに関するもの(JS,CSS,HTML,画像)を1つに
まとめたりすることができる。超高機能らしい。
2. gruntもgulpも必要ない
3. 覚えるべきことはほとんどない(らしい)
4. grunt,gulpと組み合わせることは可能
(ビルドツールとしてWebPackを使うとか)
WebPackによるJSコンパイル
webpack app/js/app.js app/assets/bundle.js
これだけでbrowserifyと同等のことをしてくれる
WebPackによるcssコンパイル
css-loader, style-loaderを入れる。
npm install --save-dev css-loader style-loader
webpack app/js/app.js app/dest/js/app.js
--module-bind “css=style!css”
ただし、cssはJSファイル内に出力される
WebPackのconfigファイルの定義
1. webpack.config.jsを定義すると、オプションが不要になる。
2. gulpのデフォルトタスクの定義と同じ
3. コマンドが”webpack”だけでよくなる。
module.exports = {
context: __dirname + '/app',
entry: './js/app.js',
output: {
path: __dirname + '/app/dest/assets/',
filename: 'app.js'
},
module: {
loaders: [
{
test: /.css$/,
loader: 'style!css'
}
]
},
devtool: 'source-map'
};
WebPackでのファイル監視
watchモードが最初からある!
webpack --watch
gulpも最初からあるが、タスク内でgulp.watchして対象ディレク
トリを指定しなければならない
webpack-dev-server
1. http://localhost:8080 でアクセスできる環境
2. livereloadの提供。
いちいちファイル更新後にリロードしないでいいのは助か
る!
ここまでの感想
1. gulpでタスクを自分で定義していくのに比べるとかなり楽!
2. ただ、es2015 => es5へのトランスパイル方法がまだわからん…
3. scssを使う方法もわからん。
4. cssがJSに吐かれるのもなんか気持ち悪いから分離したい!
5. あーあとautoprefixerがない…minifyもせんと…
各ライブラリを追加
1. babel-loader
a. WebPackでbabelを使うためのライブラリ。
b. babel-loaderとbabel-presets-ec2015があれば、トランスパイル可能
2. sass-loader
a. WebPackでsassを使うためのライブラリ。
3. extract-text-webpack-plugin(cssをjsから分離)
a. js側でcssをインポートすると、それが分離されたcssファイルになる
4. postcss-loaderを導入
a. autoprefixerプラグインを使う
5. minifyはWebPackに最初から機能がある
const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
context: __dirname + '/app',
entry: './js/app.js',
output: {
path: __dirname + '/app/dest/assets/',
filename: 'app.js'
},
devServer: {
contentBase: 'app',
publicPath: '/dest/assets/'
},
module: {
loaders: [
{
test: /.css$/,
loader: ExtractTextPlugin.extract(
"style-loader",
"css-loader!postcss-loader"
)
},
{
test: /.scss$/,
loader: ExtractTextPlugin.extract(
"style-loader",
"css-loader!sass-loader!postcss-loader"
)
},
{
test: /.js$/,
exclude: /node_modules/,
loader: 'babel',
query: { presets: ['es2015'] }
}
]
},
postcss: () => {
"use strict";
return [require('autoprefixer')]
},
plugins: [
new ExtractTextPlugin("style.css", { allChunks: true }),
new webpack.optimize.UglifyJsPlugin()
],
devtool: 'source-map'
};
gulpとwebpackのまとめと感想
1. gulpは処理(task)を書くが、webpackは設定(config)を書く、
ということがわかった。
2. WebPackは多機能で基本的にオールインワンだが、
高等なこと(es2015 => es5やsassを使うとか)をやろうとし
たらライブラリが必要
3. 設定の大変さはgulpで処理書くのとあんまり変わらないん
じゃないか?
4. タスクが散らばらずに管理できるという意味では、WebPack
はシンプル。わかりやすいかも。
まとめ
1. ES2015はシンプルに書けるし、変数スコープもあるし、良
い。
2. 最近のJSの環境構築は調べれば調べるほど、どんどん知
らないライブラリが出てきて、本来の目的を見失いそうにな
る。
3. これをするにはAがいるよ!からの、
Aはオワコン。これからはBだ!が多い。
4. そういうことに惑わされず、自分の目的が達成できるかどう
かに注力して調べる集中力がいる。じゃないと振り回され
る。

Ecmascript2015とその周辺について