ES6ではデータのプロパティをクラス直下で定義できない

[TypeScriptでクラス定数をプロパティとして定義できない件](http://qiita.com/ConquestArrow/items/494d798a4e0788c41a7c#-class)を記事にした。

理由としてはおそらくECMAScript由来の仕様だろうと思い調べてみたところ、 **ES6(ES2015)では定数に限らずデータのプロパティをクラスのブロック直下に定義できない** ことがわかった。

```javascript:babelでテスト
class Test{
	prop = 1;	//error! Unexpected token (2:6)
}
```
 * [Babelでの例](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=class%20Test%7B%0A%09prop%20%3D%201%3B%09%2F%2Ferror!%20Unexpected%20token%20(2%3A6)%0A%7D)

## ES6でのデータのプロパティの持たせ方

仕様ではクラス直下の本体部分で定義できるのはメソッドのみでデータを保持する変数や定数は定義できない。

```
ClassBody[Yield] :
	ClassElementList[?Yield]
ClassElementList[Yield] :
	ClassElement[?Yield]
	ClassElementList[?Yield] ClassElement[?Yield]
ClassElement[Yield] :
	MethodDefinition[?Yield]
	static MethodDefinition[?Yield]
	;
```
 * [14.5 Class Definitions](http://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions)

データのクラスプロパティ自体が持てない訳ではなく、メソッドの内部で`this.prop = 1;`のように定義するか、クラスのインスタンスに後からプロパティをくっつける事で出来る。

```javascript:持たせ方
class Test{
	constructor(){
		this.prop = 1;	//ok
	}
}
var a = new Test();
console.log(a.prop);	//1

a.prop2 = "str";
console.log(a.prop2); //str
```
 * [Babelでの例](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=class%20Test%7B%0A%09constructor()%7B%0A%09%09this.prop%20%3D%201%3B%09%2F%2Fok%0A%09%7D%0A%7D%0Avar%20a%20%3D%20new%20Test()%3B%0Aconsole.log(a.prop)%3B%09%2F%2F1%0A%0Aa.prop2%20%3D%20%22str%22%3B%0Aconsole.log(a.prop2)%3B%20%2F%2Fstr)

クラス定数も出来ない事は無い。

```javascript:クラス定数っぽいもの
class Test{
	constructor(){
	}
	
	get MAX(){
	  return 10000;
	}
}
var a = new Test();
console.log(a.MAX); //10000
a.MAX = 0;  //error! 代入不可
```

## TypeScriptでは

TypeScriptではクラスのデータプロパティをクラスの直下で定義できる。さらに、`public`/`private`/`protected`等のアクセス修飾子でアクセスの制御もできる。

OOPをやるならカプセル化のためにアクセス制御が必要なのでECMAScript側の仕様が固まる前に実装しているのだろう[^1]。まあ、普通。

```javascript:TypeScript
class Test{
	prop = 1;	//ok
	static prop2 = 3;	//ok
	private prop3 = 1;	//ok
	protected prop4 = 1;	//ok
}
class Test2 extends Test{
	get prop5(){
		return this.prop4;
	}
}
var a = new Test();
console.log(a.prop);	//1
console.log(Test.prop2);	//3
console.log(a.prop3);	//error!
console.log(a.prop4);	//error!
var b = new Test2();
console.log(b.prop4);	//error!
console.log(b.prop5);	//1
```
 * [TypeScript PlayGround](http://www.typescriptlang.org/Playground#src=class%20Test%7B%0A%09prop%20%3D%201%3B%09%2F%2Fok%0A%09static%20prop2%20%3D%203%3B%09%2F%2Fok%0A%09private%20prop3%20%3D%201%3B%09%2F%2Fok%0A%09protected%20prop4%20%3D%201%3B%09%2F%2Fok%0A%7D%0Aclass%20Test2%20extends%20Test%7B%0A%09get%20prop5()%7B%0A%09%09return%20this.prop4%3B%0A%09%7D%0A%7D%0Avar%20a%20%3D%20new%20Test()%3B%0Aconsole.log(a.prop)%3B%09%2F%2F1%0Aconsole.log(Test.prop2)%3B%09%2F%2F3%0Aconsole.log(a.prop3)%3B%09%2F%2Ferror!%0Aconsole.log(a.prop4)%3B%09%2F%2Ferror!%0Avar%20b%20%3D%20new%20Test2()%3B%0Aconsole.log(b.prop4)%3B%09%2F%2Ferror!%0Aconsole.log(b.prop5)%3B%09%2F%2F1)

## Babelでは

experimentalフラグをONにすればいける。[ES.nextで仕様検討中](https://github.com/jeffmo/es-class-fields-and-static-properties)のため。

```javascript:
class Test{
	prop = 1;	//ok, experimental on
}
```
 * [Babel](https://babeljs.io/repl/#?experimental=true&evaluate=true&loose=false&spec=false&code=class%20Test%7B%0A%09prop%20%3D%201%3B%09%2F%2Fok%2C%20experimental%20on%0A%7D%0A)

なお、フラグをONにしなくても、値を代入しない定義だけならエラーにならなかった。これがES6の仕様に即しているのかはわからない。

```javascript:babelでエラーにならないけど合ってる？
class Test{
	prop;
}
```
 * [Babelではエラーにならない例](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=class%20Test%7B%0A%09prop%3B%0A%7D%0A)

## 以下、愚痴。何が問題なのか。

 * `this.prop`で宣言するのはインデントが一つ深いのでパッと見理解しにくい
  * 同一スコープなんだから、同じインデントにしようよ
 * 他のクラスベースの言語に合わせようよ
  * 使いにくいオリジナリティとか要らないから
 * クラスレベルのスコープの定数が定義しにくい
  * ただでさえスコープ制御が貧弱なんだからもう少しなんとかならなかったの？
  * `Number.MAX_SAFE_INTEGER`みたいなのを簡単に定義させてほしい
 * アンチパターンとしてクラスプロパティの宣言時に値を入れておきたくないというのはわかる
  * staticなプロパティや定数までできなくしたのはやり過ぎじゃ？
  * そのくせ宣言後のクラスインスタンスにプロパティ生やせちゃうのは…どうにかならなかったん？
 * IDEの補完にむいてなさそう
  * 最近の各IDEの補完は賢いからこれは杞憂に終わるかも 
 * ES.nextー！早く来てくれー！


[^1]: ただ何故かクラス定数は簡単に定義できない。