細かすぎて伝わらないJavaScript芸人やります。
まず二つのものについて紹介します。
Immutable.js というのがあります。これはいわゆる不変性を保ってくれるクラスの集まりです。 いわゆる関数型言語の値のようにlistやkey-value型のmapをこねこねできます。この中にRecordというクラスがあり、これは言わば限定されたプロパティのみを持つmapです。JSのPlain Objectだと発生してしまう副作用を防ぐことができます。例えば、
class Point extends Immutable.Record({ x: 0, y: 0}) { .. } const a = new Point({ x: 1, y: 2 }); const b = a.set('x', 3); console.log(b.x); // => 3 console.log(a.x); // => 1 (変わってない)
なんてことができます。やったね。
Class Property Transformというのがあります。Babelについては省略します。このモジュールはクラスのクラス変数/メンバ変数/クラスメソッド等を定義可能にするbabelの変換です。これのメリットは、JSの .bind(this)
を不要にしてくれる点です。例えば、
class Foo extends React.Component { handleClick() { console.log(this.props.foo); } render() { return <Button onClick={this.handleClick.bind(this)} />; } }
と書かなければならない所を
class Foo extends React.Component { handleClick = () => { console.log(this.props.foo); } render() { return <Button onClick={this.handleClick} />; // bindがいらない } }
と書けるようになります。
ガッチャンコ!!
てなわけで、こんなことしますよね。
class Point extends Immutable.Record({ x: 0, y: 0 }) { length = () => Math.sqrt(this.length2()); length2 = () => this.x * this.x + this.y * this.y; }
いい感じです。
const a = new Point({ x: 4, y: 3 }); console.log(a.length()); // => 5
いいですね〜!
const a = new Point({ x: 4, y: 3 }); const b = a.set('y', 6); console.log(b.length()); < Uncaught TypeError: b.length is not a function
え?
console.log(a instanceof Point); // => true console.log(b instanceof Point); // => true console.log(a.length); // => () => ...; console.log(b.length); // => undefined
ダメだった
どうやら、Class Property Transformはメンバ変数の定義をconstructorの中に展開するんですね。つまり、
class Point extends Immutable.Record({ x: 0, y: 0 }) { constructor(...args) { super(...args); this.length = () => Math.sqrt(this.length2()); this.length2 = () => this.x * this.x + this.y * this.y; }
に近いコードに置き換えられるようです。しかし、ImmutableJSは .set
などのメソッドで新しいインスタンスを作る時に、new
ではなく Object.create
を使うようです。それゆえに、constructorは評価されなくなり、メソッドが無いことになってしまう、ということらしいです。なんとかならないかとImmutableJSを見に行ったけど、issueが一蹴されていたので多分ムリですな。