最近、ES6ばっかり書いていて完全に止められない状態になってしまっているのですが、ES5で書いていたときからプログラムの書き方自体が変わってきたなと感じる部分があるので列挙してみます。
Destructuring assignments on formal argument
分割代入のシンタックス
let obj = { a: 100, b: 200, c: 300 };
let { a, b, c } = obj;
console.log(a, b, c); //-> 100, 200, 300
これをメソッドとかコールバックの仮引数で使う。
emitter.on("noteOn", ({ noteNumber, velocity }) => {
// ここでなにか処理する
});
最初は引数の順番を気にしなくて良いとか、あとから雑な気持ちで引数を増やせて便利だなくらいに思っていたのだけど、最近はイベント発行側が受け取り側の変数名を束縛できるのでコードに一貫性を持たせられるというのが実はけっこう良いのではと思っている。
Symbol
2015/07/28 13:45 記述修正
外部からアクセスしにくい、もしくは重複しない変数を作るのに使っている。
import { EventEmitter } from "events";
export const EVENTS = typeof Symbol !== "undefined" ?
Symbol("EVENTS") : "@mohayonao/${package-name}:EVENTS";
export default class SummerVacation extends EventEmitter {
constructor() {
super();
this[EVENTS] = [];
}
pushEvent(event) {
this[EVENTS].push(event);
}
}
Symbolはユニークなので、こうしておくと気軽にインスタンス変数にアクセスできなくなるだけでなく、継承元が使っているインスタンス変数との重複を気にしなくて良くなる。上の例だと夏休みのイベントを管理するのに this._events
を使ってしまうと EventEmitter が使っているのと被るので良くない。
export
してあるのはテストで触れた方が楽なことがあるから。外部から気軽にアクセスできない方がよいけど、きちんと手順を踏めば別に触っても良いよというスタンス。
Computed Property
メソッド命名規則を無視した特殊なメソッドを作るのに使っている。例はわかりにくいけど反応したいイベントハンドラを "/" 始まりのルールで記述しておいて実行できそうならするパターン。たとえば特定の URL パターンに反応させるとかが簡単に書ける。
class Delegator {
delegate(address, data) {
if (address[0] === "/" && typeof this[address] === "function") {
this[address](data);
}
}
}
let store1 = new class extends Delegator {
["/read/succeeded"](data) {}
["/read/failed"](data) {}
};
let store2 = new class extends Delegator {
["/read/failed"](data) {}
};
function doAction(stores, address, data) {
stores.forEach((store) => {
store.delegate(address, data);
});
}
doAction([ store1, store2 ], "/read/failed", new TypeError());
flux アーキテクチャで実際に試してみた例
https://github.com/mohayonao/fluxx/blob/master/examples/todomvc/stores/TodoStore.js
他にはオブジェクトのプロパティを16進数で書きたいときにも便利
const MIDIMessage = {
[0x80] : "noteOff",
[0x90] : "noteOn",
};
Selective SuperClass
たとえば、node.js でも ブラウザでも使える MIDIKeyboard を扱うクラスというのを考えた時に一般的なクラスツリーだと以下のように考えると思う。
このとき NodeMIDIKeyboard と WebMIDIKeyboard は同じMIDIメッセージパーサーを参照して同じような処理を2回書く必要があるのだけど、やることが全く同じなら以下のように親クラスを選択できたほうが簡単。ハードウェアに近い部分のAPIが異なる JavaScript の問題を上手く解決できていると思う。
function _extends(MIDIDevice) {
return class MIDIKeyboard extends MIDIDevice {
// クラスの実装を書く
};
}
export default { extends: _extends };
import WebMIDIDevice from "@mohayonao/midi-device/webmidi";
import MIDIKeyboard from "./MIDIKeyboard";
export default MIDIKeyboard.extends(WebMIDIDevice);
import NodeMIDIDevice from "@mohayonao/midi-device";
import MIDIKeyboard from "./MIDIKeyboard";
export default MIDIKeyboard.extends(NodeMIDIDevice);
実際に試してみた例