情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

ECMAScript 6の新機能(21)クラス

クラス

ECMAScript6でクラスが追加されました。

クラスは下のように作成します。

C:\work\node>node
> class Person {
... constructor (name, age) {
..... this.name = name;
..... this.age = age;
..... }
... toString () {
..... return 'name: ' + this.name + ' age: ' + this.age;
..... }
... static def () {
..... return new Person('tarou', 10);
..... }
... }
[Function: Person]

インスタンスメソッドは何もキーワードを付加しない関数として定義し、コンストラクタはconstructorという名称の特別な関数として定義し、クラスメソッドは関数名の前にstaticキーワードを付加して定義します。

また、ゲッターは関数名の前にgetキーワードを付加して定義し、セッターは関数名の前にsetキーワードを付加して定義します。

クラスにはメソッド、ゲッター、セッター以外のプロパティを含めることはできませんので注意してください。

クラスをインスタンス化するにはnewキーワードを使用します。

> var p = new Person('yumina', 26)
undefined
> p.toString()
'name: yumina age: 26'
> p.name
'yumina'
> p.age
26

クラスの定義においてコンストラクタを指定しなかった場合には下のようなコンストラクタが使用されます。

constructor () {}

コンストラクタで値を返さなかった場合にはthisが返り、オブジェクト以外の値を返した場合にはその値は無視され、thisが返り、オブジェクトを返した場合にはそのままそのオブジェクトが返ります

> class C1 {
... constructor () {
..... return 1;
..... }
... toString () {
..... return 'c1';
..... }
... }
[Function: C1]
> var c1 = new C1()
undefined
> c1.toString()
'c1'
> class C2 {
... constructor () {
..... return { foon: 100, barn: 200 };
..... }
... toString () {
..... return 'c2';
..... }
... }
[Function: C2]
> var c2 = new C2()
undefined
> c2.toString()
'[object Object]'
> c2.foon
100
> c2.barn
200

クラスの型はfunctionです。

> typeof Person
'function'

しかし、クラスを普通の関数として呼び出すことはできませんので注意してください。

> Person()
TypeError: Class constructor Person cannot be invoked without 'new'
    at repl:1:1
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

また、クラスは普通の関数とは異なり、巻き上げ(ホイスティング)は行われませんので注意してください。

C:\work\node>node
> function fo () {
... fi();
... function fi () {
..... console.log('function');
..... }
... }
undefined
> fo()
function
undefined
C:\work\node>node
> function fo () {
... var c = new C();
... class C {
..... constructor () {
....... console.log('class');
....... }
..... }
... }
undefined
> fo()
ReferenceError: C is not defined
    at fo (repl:2:15)
    at repl:1:1
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)

なお、巻き上げが行われないということはコード上クラスが宣言されるより上でそのクラスを使用できないという訳ではないことに注意してください。

たとえば、下のようなことは可能です。

C:\work\node>node
> function fo () {
... function fi () {
..... var c = new C();
..... }
... class C {
..... constructor () {
....... console.log('class');
....... }
..... }
... fi();
... }
undefined
> fo()
class
undefined

また、クラスは普通の関数とは異なり、同名のものを複数定義することはできませんので注意してください。

C:\work\node>node
> function f () {
... function f1 () {console.log('f1-1');}
... function f1 () {console.log('f1-2');}
... f1();
... }
undefined
> f()
f1-2
undefined
C:\work\node>node
> function f () {
... class C {}
... class C {}
SyntaxError: Identifier 'C' has already been declared
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

ただし、クラスの定義を変更することはできます。

C:\work\node>node
> class C {}
[Function: C]
> C
[Function: C]
> new C()
C {}
> C = null
null
> C
null
> new C()
TypeError: C is not a constructor
    at repl:1:1
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:224:10)
    at REPLServer.Interface._line (readline.js:566:8)
    at REPLServer.Interface._ttyWrite (readline.js:843:14)

また、クラスは普通の関数とは異なり、グローバルスコープで定義してもグローバル変数(クラス)になることはありませんので注意してください。

C:\work\node>node
> function f () {
... console.log('f');
... }
undefined
> global.f
[Function: f]
> global.f()
f
undefined
> class C {}
[Function: C]
> global.C
undefined

evalargumentsという名称のクラスを定義することはできません。

C:\work\node>node
> class eval {}
SyntaxError: Unexpected eval or arguments in strict mode
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:224:10)
    at REPLServer.Interface._line (readline.js:566:8)
    at REPLServer.Interface._ttyWrite (readline.js:843:14)
> class arguments {}
SyntaxError: Unexpected eval or arguments in strict mode
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:224:10)
    at REPLServer.Interface._line (readline.js:566:8)
    at REPLServer.Interface._ttyWrite (readline.js:843:14)

クラスのインスタンスの型はobjectです。

> typeof new class C {}()
'object'

クラスの定義によって作成されるプロパティの属性はプロパティの種類に応じて下のようになります。ここでCは任意のクラスを表し、*は任意のプロパティを表します。

writable enumerable configurable
C.* true false true
C.prototype false false false
C.prototype.constructor false false true
C.prototype.* true false true

派生クラス

派生クラスは下のように作成します。

C:\work\node>node
> class Person {
... constructor (name, age) {
..... this.name = name;
..... this.age = age;
..... }
... toString () {
..... return 'name: ' + this.name + ' age: ' + this.age;
..... }
... static def () {
..... return new Person('tarou', 10);
..... }
... }
[Function: Person]
> class PersonEx extends Person {
... constructor (name, age, address) {
..... super(name, age);
..... this.address = address;
..... }
... toString () {
..... return super.toString() + ' address: ' + this.address;
..... }
... static defex () {
..... return new Person('tarou', 10, 'newyork');
..... }
... }
[Function: PersonEx]
> PersonEx.def()
Person { name: 'tarou', age: 10 }
> PersonEx.defex()
PersonEx { name: 'tarou', age: 10, address: 'newyork' }
> var pex = new PersonEx('yumina', 26, 'shibuya')
undefined
> pex.toString()
'name: yumina age: 26 address: shibuya'
> pex.name
'yumina'
> pex.age
26
> pex.address
'shibuya'
> pex instanceof PersonEx
true
> pex instanceof Person
true

基底クラスのクラスメソッドは派生クラスのクラスメソッドにもなります。

派生クラスのコンストラクタで基底クラスのコンストラクタを呼び出すにはsuperを関数として使用します。

派生クラスのインスタンスメソッドやゲッターやセッターで基底クラスのインスタンスメソッドやゲッターやセッターを呼び出すにはsuperを基底クラスのインスタンスとして使用します。

派生クラスのクラスメソッドで基底クラスのクラスメソッドを呼び出すにはsuperを基底クラスとして使用します。

派生クラスのコンストラクタでは必ず1回のみsuperを呼び出さなければなりません。また、少なくともthisにアクセスする前に呼び出さなければなりません。

ただし、派生クラスのコンストラクタでオブジェクトを返した場合にはこの限りではありません。

C:\work\node>node
> class C {}
[Function: C]
> class CEx extends C {
... constructor () {
..... this.x = 100;
..... }
... }
[Function: CEx]
> var cex = new CEx()
ReferenceError: this is not defined
    at CEx (repl:3:1)
    at repl:1:13
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
C:\work\node>node
> class C {}
[Function: C]
> class CEx extends C {
... constructor () {
..... super();
..... super();
..... this.x = 100;
..... }
... }
[Function: CEx]
> var cex = new CEx()
ReferenceError: this is not defined
    at new CEx (repl:4:1)
    at repl:1:13
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)

派生クラスの定義においてコンストラクタを指定しなかった場合には下のようなコンストラクタが使用されます。

constructor (...args) {
    super(...args);
}

なお、基底クラスでsuperを使用することはできません。

C:\work\node>node
> class C {
... constructor () {
..... super();
SyntaxError: 'super' keyword unexpected here
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

無名クラス

関数における無名関数と同様にクラスにおいても無名クラスを定義することができます。

C:\work\node>node
> var Person = class {
... constructor (name, age) {
..... this.name = name;
..... this.age = age;
..... }
... toString () {
..... return 'name: ' + this.name + ' age: ' + this.age;
..... }
... static def () {
..... return new Person('tarou', 10);
..... }
... }
undefined
> Person.def()
{ name: 'tarou', age: 10 }
> var p = new Person('yumina', 26)
undefined
> p.toString()
'name: yumina age: 26'
> p.name
'yumina'
> p.age
26

クラスの内部名

クラスに名前を付けた場合(無名クラスでないクラスを定義した場合)にはクラス中ではその名前をクラスを参照するために常に使用することができます。

C:\work\node>node
> class C {
... foon () {
..... console.log(C.qu);
..... }
... }
[Function: C]
> C.qu = 'quu'
'quu'
> const D = C
undefined
> C = null
null
> var d = new D()
undefined
> d.foon()
quu
undefined
> D.qu
'quu'
> C.qu
TypeError: Cannot read property 'qu' of null
    at repl:1:2
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)
pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-ecmascript 2015, ecmascript 6, Javascript