Web DB Press vol.87のES6の特集を読みながら、ES6を勉強した。とてもわかりやすかった。
紹介されている機能をちょこちょこと試す細かいスクリプトを色々書いたりメモを取ったりしたのでまとめておく。色々な機能があった。

https://github.com/shokai/es6-study

最近Web MIDI APIを使ってたんだけど、そのインタフェースがES6のPromiseとfor-ofで回す新しいIteratorとTypedArrayで実装されていたので、今後ブラウザに増える新しいAPIがES6前提で作られるなら普段からES6で書くようにしたほうがいいかなと思った。

個人的にはcoffeeの方がES6よりちょっと好き。でもfor-of文がES6で新しく増えたMapやIteratorを使う時に重要なんだけど、coffeeのfor-of文は別の意味があるので、coffeeからES6のAPIを使うのは辛そうな気がしている。一応IteratorはforEachで回せるけど


ES6の機能メモ

(coffeeとの比較みたいな感想入り)

Promise

非同期処理の新しいインタフェース。ScalaのFutureみたいなやつ。

変数の初期化

var foo = "zanmai";
var bar = "kazusuke";
var obj = {foo, bar};
// オブジェクト初期化省略記法
// {foo: "zanmai", bar: "kazusuke"} というオブジェクトができる

分割代入

var [foo, bar] = ["zanmai", "kazusuke"]; // 配列を配列で受けるとそれぞれ変数に代入できる
[a, b] = [b, a]; // 入れ替えもできる

class構文がある

extendsとかもできる。superで呼び出す
ほぼcoffeeのclassと同じ感じ
static/setter/getterを設定する修飾子がある
 class User extends EventEmitter2{
constructor(name){
super({delimiter: ":", wildcard: true}); // superで継承元を呼び出す
this.name = name; // プロパティを持てる
}
get website(){ // getter設定
return "http://github.com/"+this.name;
}
}
組み込みクラス(Array等)も継承で拡張できる
これまでArrayなどにメソッドを生やしたりすると全体に影響が及んでいたのが解決できる

Arrow Function

 var sum = (a, b) => {
return a + b;
}
functionを=>で書ける
外側のthisが引き継がれる
coffeeの=>と同じ
thisを引っ張ってきたくなければ今まで通りfunctionを使う


Generatorがある

function()*{ yield foo; }
値を複数回返せる機構
returnではなくyieldで返す
Rubyのblock+yieldと同じ

requireのかわりにexport/import

requireの挙動が複雑だったので仕様を直したもの
babelではrequireに変換される
export function foo(){ ~~~~ };
// module.exports.foo = function(){ ~~~ }; と同じ

export default function(){ ~~~ };
// defaultを付けると、module.exports = function(){ ~~~ }; と同じ

import foo from "foo";
// var foo = require("foo"); と同じ

import {EventEmitter2} from "eventemitter2";
// 部分的にrequireできる。var EventEmitter2 = require("eventemitter2").EventEmitter2; と同じ
// coffeeでrequireの左辺に{ }付けた時と同じ挙動

import {aaa, bbb} from "foo";
// 複数の子をまとめてrequireできる
// var aaa = require('foo').aaa;
// var bbb = require('foo').bbb; と同じ
classを1つだけ提供する時は
export default class ClassName { ~~ } で提供し
import className from “ClassName”; で読み込む

ユーティリティ関数を複数提供する時は、exportとimport {name}で部分的に読み込めるようにすると良さそう?

色々と関数が追加されている

String.repeat(num); // 文字列をくり返す
Array.from(obj); // 新規Arrayインスタンス作成
Object.assign(target, source1, source2...); // targetにsourceのプロパティをmix-inする

テンプレート文字列がある

`my name is ${this.name}`
バッククオートで囲う
${}の中が評価される

タグ付きテンプレート文字列

例えば
gyazz`gyazz記法を[[展開]]できる`
と書くと、 gyazz記法を<a href=”./展開”>展開</a>できる
function gyazz(templates, …values){ 処理して文字列を返す }
のような関数を宣言しておけば使える
`の前に関数名を書いて変換器を指定する
function gyazz(templates, ...values){
console.log("---gyazz markup start---");
var reg = /\[{2}([^\[\]]+)\]{2}/g;
var str = "";
for(var i = 0; i < templates.length; i++){
str += templates[i] || "";
str += values[i] || "";
}
if(!reg.test(str)){
return str;
}
return str.replace(reg, "<a href=\"./$1\">$1</a>");
}

2/8/16進数

0b1010
0xFA
0o70
接頭辞で書ける

デフォルトパラメータ

 var foo = function(name="shokai"){ console.log(name); };
foo("hoge"); // => hoge
foo(); // => shokai
引数なしの時の初期値を設定できる

可変長引数

var foo = function(...bar){ (略) }
barをArrayとして受け取れる
変数名の前に...を付ける
es5では特殊変数argumentsを見なければできなかった
関数宣言時に書けるのでぱっと見わかりやすい
静的解析ツールの支援も受けやすそう

スプレッドオペレータ

[1,2, ...[5,6,7]] // [1,2,5,6,7] になる

// Underscoreのrangeと組み合わせると強い
[..._.range(1,5), 11, 12, ..._.range(20,23)] // [1,2,3,4,5,11,12,20,21,22,23] になる

var [a,b,...c] = [1,2,3,4,5,6,7]; // 分割代入と組み合わせ
// a => 1
// b => 2
// c => [3,4,5,6,7]

定数

 const NAME = "shokai";
NAME = "zanmai"; // 代入できない
console.log(NAME); // => shokai

ブロックスコープ変数

letで宣言する
 if(true){
let name = "shokai";
console.log(name); //=> shokai
}
console.log(name); // name is not defined
for(let i = 0; i < 10; i++){ // varで宣言すると全部10になる
setTimeout(function(){
console.log(i); // 1,2,3,4,5,6,7,8,9,10
});
}
letがあるので、coffeeのdo (args) -> が必要なくなる


イテレータ

for(let i of arr){ console.log(i); }
// MapやSetはkey, valueで回せる
for(let [k,v] of map){ }

TypedArray

型付き配列
var arr = new Uint8Array(8); // 8bit型
arr[0] = 256; // これは8bit超えてるので代入されない。例外は発生せず代入処理だけが実行されない
arr[1] = 255; // 代入できる
WebGLやWebMIDIなど大量のデータを処理する時に速い
Nodeのbuffer由来
length等いくつかのプロパティが無い
Arrayに変換すればok
var arr2 = Array.prototype.slice.call(arr);

Proxy

Rubyのmethod missingと同等の事ができる
現状Firefoxでしか動かない?


実行環境・ツール


ES6をそのまま実行できる環境


ローカルで実行するちょっとしたスクリプトとかだと、いちいちコンパイルせずにES6のまま実行できると良い

Chrome
だいたいそのままes6が動く
Arrow Function以外
Chrome拡張とか作るならいきなりES6で書いても良さげ

Firefox
だいたいそのままes6が動く
Proxyもたしか動くはず
classが無い

Node.js
0.12なら--harmonyオプションを付けるとes6が実行できる
rest parameterやspread operatorが使えない

iojs
class, Generatorなど多くの機能をサポートしてる
--harmonyオプションを付ければもっと色々使える
--harmony_rest_parametersや--harmony_arrow_functionsなども必要に応じてONにできる
サーバーで実行するならiojsで良さそう。herokuでもengine指定すれば使える

babel/register
babelに含まれているpolyfill
ブラウザでもサーバーでも動くのでわりと良さそう
プログラムの頭で
require("babel/register")();
しておくと、以後ES6コードをrequireするとふつうに実行できる
Nodeのrequire関数がフックされて拡張されるらしい
最初のregisterするファイルにはes6を書けない、syntax errorになる
Node0.10でもfor-of文が動かせる

babel-node
babel付属の実行コマンド
es6を直接実行できる
Node0.10でもfor-of文が動かせる

osascript
MacのAppleScript実行コマンドだったが、YosemiteでJavaScriptでも書けるようになった。ES6が一部サポートされている。
分割代入とspread operatorとMapとSetが動く。
Promiseはオブジェクトはあるけど呼び出すとエラーが返る

ES6をふつうのJavaScript(ES5)に変換するツール


色々なブラウザ環境で実行させるとしたらES5に変換するのが現実的

babel
es6をes5に変換するトランスパイラ
元6to5
node 0.12なら問題なし、node 0.10でもiteraotr系以外動くJSが作れる
ES6でライブラリを書いてnpmに公開するとかにオススメ
新し目のブラウザでの実行もbabelで良さそう

traceur
es6をes5に変換
Google製

Browserify+babelify
es6からブラウザjsに変換、requireを辿って1ファイルに固めてくれるのでnode_modulesも読み込める

lint

ESLint+babel-eslint
es6をlintできる


ES6を実際使う

ES6は各ブラウザでもサポートしている機能としてない機能がバラバラなのでbabelでES5に変換してやるのが良いようだ。
ただ、for-ofのiteratorはbabelで変換すると変換後のコードにSymbol.iteratorが含まれているのでNode 0.10だと動かない。Node 0.12だと動く。

あとProxyはFirefoxでしか動かないっぽい。

色々試した感じ、例えばES6で書いてbabelで変換してからNodeのライブラリとしてnpmで配布する時はfor-ofだけ使わないように意識すれば事故は起こらないはず。
(MapやTypedArrayみたいな新しい組み込み型は使わないとして)


ES6と変換後のES5の動作をブラウザでも手軽に確かめたかったので、ちょっとしたツールを作った。
こんなかんじでgithubに置いてあるES6/ES5が実行できる

Herokuでも動いてて、サーバーはES6で書いてbabel-nodeで実行してる
https://shokai-es6-study.herokuapp.com/