ES Class Fields (Stage 2 now)
プライベートフィールドがハッシュな変態記法なのは何でなんだぜ?
class Point {
#x;
#y;
constructor(x = 0, y = 0) {
#x = x;
#y = y;
}
get x() { return #x }
get y() { return #y }
area() {
return #x * #y;
// return this.#x * this.#y;
}
equals(p) {
return #x === p.#x && #y === p.#y;
}
toString() {
return `Point<${ #x },${ #y }>`;
}
}
- Encapsulation または hard private がゴール
- privateフィールドが存在すること自体をクラスの外部から気づかれないようにしたい
- そうでない(soft private)なら
Symbol
で事足りるので存在意義がない
- リフレクションもできない
- サブクラスからもprivateにアクセスできない
- オブジェクトの内外からprivateフィールドと同名で別のプロパティを定義可能である必要がある
- もしエラーになったりするとprivateフィールドの存在がばれてしまう
class Foo {
#bar = 1;
}
const foo = new Foo();
foo.bar = 2; // Works!
class Bar extends Foo {
bar = 3; // Works!
}
- そのシンタックスの言語はJavaとかだいたい静的型付け言語
- 動的型付け言語の場合、これを
this.foo
と呼び出したときにprivateかどうか不明なのでルックアップが走る
// this.fooが参照されるたびに走る処理系の動きのイメージ(疑似コード)
if (
otherPoint instanceof Point &&
isNotSubClass(otherPoint, Point)
) {
return getPrivate(otherPoint, 'foo');
} else {
return otherPoint.foo;
}
- これが 任意のプロパティドットアクセス で走るのは、さすがにやばい
- よって
this.foo
のような従来使われているシンタックスで参照させられない - つまり、呼び出し時になんらかの特殊なprefix等が必要なので、宣言時に
private foo
できても嬉しさ半分となる
- breaking changeになるから
- ドットアクセス限定で
- ASI hazard!
- ASI: Auto Semi-colon Insersion
- JSの忌まわしき仕様、自動セミコロン挿入の挙動が壊れる
- すなわちbreaking change
#
is the most beautiful!
@
: taken by ES Decorators_
,$
: breaking change%
,^
,&
,?
: ASI hazard- non ascii: 打ちづらいでしょ
- 見えちゃうとユーザーが使っちゃう、ライブラリ作者は激おこ
- すでにclosureや
WeakMap
でhard privateは実現可能だが、使いづらい
- closureはclass単位のprivate field を作れない (冒頭コード例の
foo.equals(bar)
とか) WeakMap
は遅い (筆者注: ほんと?)
- 見えるやつなら、Symbolで実現できるしね
hard private以外はDecoratorsを使えばいろいろできるんじゃないか?
- encapsulationをゴールとする場合、後方互換性、他の仕様との整合性、実装時のパフォーマンスを考慮すると、ほぼ消去法的に
this.#foo
しかないという結論
今月のTC39でStage 3に進んだ: tc39/proposals@0bbbb08