javaScriptのオブジェクト指向(ES6)

フロントエンド

ES6のオブジェクト指向の基礎的なところをまとめました。

クラスの定義、インスタンスの生成

Norimonoクラスを定義します。Norimonoクラスは定員を表すcapacityプロパティを持っています。

class Norimono {
    constructor(capacity) {
        this.capacity = capacity;
    }
}

let norimono = new Norimono(10);
console.log(norimono);
Norimono { capacity: 10 }

コンストラクタで定員を初期化し、インスタンスの生成ができました。

アクセサーメソッドの定義

ゲッターとセッターを作ってみます。

class Norimono {
    constructor() {
        this.capacity;
    }
    
    getCapacity() {
        return this.capacity;
    }
    
    setCapacity(capacity) {
        this.capacity = capacity;
    }
}

let norimono = new Norimono();
norimono.setCapacity(20);
console.log(norimono.getCapacity());
20

capacityをコンストラクターではなくセッター経由で設定するようにしました。しかし、capacityプロパティはpublicなのでアクセサーメソッドを作る意味はないです。

⬇ためしにクラス外部からcapacityプロパティにアクセスしてみます。

class Norimono {
    constructor() {
        this.capacity;
    }
    
    getCapacity() {
        return this.capacity;
    }
    
    setCapacity(capacity) {
        this.capacity = capacity;
    }
}

let norimono = new Norimono();
norimono.setCapacity(20);
console.log(norimono.capacity);
20

メソッドの定義

class Norimono {
    constructor(capacity) {
        this.capacity = capacity;
    }
    
    sayCapacity() {
        console.log('定員は' + this.capacity + '人です');
    }
}

let norimono = new Norimono(10);
norimono.sayCapacity();
定員は10人です

get、set

ES6からget構文とset構文が追加されました。

⬇Norimonoクラスに、乗車(take)メソッドと、乗務員を返す(getAllPeoples)メソッドを実装してみます。

class Norimono {
    
    constructor(capacity) {
        this.capacity = capacity;
        this.peoples = [];
    }
    
    getAllPeoples() {
        return this.peoples.join(' ');
    }
    
    take(people) {
        if (this.capacity <= this.peoples.length) {
            console.log( '定員オーバーのため' + people + 'は乗車できません');
        } else {
            this.peoples.push(people);
        }
    }
}

let norimono = new Norimono(3);
norimono.take('ノビタ'); 
norimono.take('シズカ');
norimono.take('スネオ');
norimono.take('ジャイコ');
console.log(norimono.getAllPeoples());
定員オーバーのためジャイコは乗車できません 
ノビタ シズカ スネオ

⬇これをget構文、set構文で書き換えます。

class Norimono {
    
    constructor(capacity) {
        this.capacity = capacity;
        this.peoples = [];
    }
    
    get allPeoples() {
        return this.peoples.join(' ');
    }
    
    set take(people) {
        if (this.capacity <= this.peoples.length) {
            console.log( '定員オーバーのため' + people + 'は乗車できません');
        } else {
            this.peoples.push(people);
        }
    }
}

let norimono = new Norimono(3);
norimono.take = 'ノビタ'; 
norimono.take = 'シズカ';
norimono.take = 'スネオ';
norimono.take = 'ジャイコ';
console.log(norimono.allPeoples);
定員オーバーのためジャイコは乗車できません
ノビタ シズカ スネオ

少しだけコードがすっきりしましたね。get構文を使えば、プロパティを参照するような感覚でメソッドを呼び出すことができます。set構文では、プロパティに直接代入するような感覚でメソッドを呼び出すことができています。

get、setは、クラス配下だけでなくオブジェクト配下でも利用できる構文です。

静的プロパティ、静的メソッド(クラスメソッド)

static修飾子をつけてメソッド定義をすると静的メソッドになります。

class Norimono {
    static sayMessage() {
        console.log('発進しまーす');
    }
}

Norimono.sayMessage();
発進しまーす
静的メソッドとは?

通常のインスタンスメソッドはインスタンス毎にメソッドを持ちますが、クラスで唯一のメソッドのことを静的メソッドといいます。インスタンスプロパティを参照しないメソッドはすべて静的メソッドにすることが可能です。

インスタンス経由で参照すると?

インスタンス経由で静的メソッドを呼ぼうとするとエラーになります。

class Norimono {
    static sayMessage() {
        console.log('発進しまーす');
    }
}

Norimono.sayMessage();
var norimono = new Norimono();
norimono.sayMessage();
TypeError: norimono.sayMessage is not a function

インスタンス内部からの参照

静的メソッドをインスタンス内部から参照すると?

class Norimono {
    static sayMessage() {
        console.log('発進しまーす');
    }
    callSayMessage() {
        sayMessage();
    }
}

var norimono = new Norimono();
norimono.callSayMessage();
ReferenceError: sayMessage is not defined

エラーになってしまいました。
⬇以下のように書き換えると動作します。

class Norimono {
    static sayMessage() {
        console.log('発進しまーす');
    }
    callSayMessage() {
        Norimono.sayMessage();
    }
}

var norimono = new Norimono();
norimono.callSayMessage();

クラス内部からの参照なのに、静的メソッドは「クラス名.メソッド名()」の形式でないと呼び出すことができません。他の言語ではこのような仕様はあまりないので混乱ポイントです。

静的プロパティ

静的プロパティも同じようにstatic修飾子をつけてクラス配下に定義します。

class Norimono {
    static message = '発進しまーす';
}

console.log(Norimono.message);
発進しまーす

継承

Norimonoクラスを継承したCarクラスを作ります。

class Norimono {
    constructor(capacity) {
        this.capacity = capacity;
    }
    
    sayCapacity() {
        console.log('定員は' + this.capacity + '人です');
    }
}

class Car extends Norimono {
    constructor(capacity) {
        super(capacity);
    }
}

var car1 = new Car(10);
var car2 = new Car(5);
car1.sayCapacity();
car2.sayCapacity();
定員は10人です 
定員は5人です

extendsを使うことで継承ができます。superは親のコンストラクタを呼び出すときに使います。superをコンストラクタ内で使用するときは先頭でなければいけません。

子クラス独自のプロパティを定義する

Carクラスだけに車の種類のプロパティと、それをコンソール出力するメソッドを定義します。

process.stdin.resume();
process.stdin.setEncoding('utf8');
// Your code here!


class Norimono {
    constructor(capacity) {
        this.capacity = capacity;
    }
    
    sayCapacity() {
        console.log('定員は' + this.capacity + '人です');
    }
}

class Car extends Norimono {
    constructor(capacity, type) {
        super(capacity);
        this.type = type;
    }
    
    sayType() {
        console.log('タイプは' + this.type + 'です');
    }
}

var car1 = new Car(10, 'セダン');
var car2 = new Car(5, 'ワゴン');
car1.sayCapacity();
car1.sayType();
car2.sayCapacity();
car2.sayType();
定員は10人です 
タイプはセダンです 
定員は5人です 
タイプはワゴンです

オーバーライドの方法

親クラスのメソッドを子クラスで上書きすることをオーバーライドといいます。Norimonoクラスで定義しているsayCapacityメソッドをオーバーライドしてみます。

class Norimono {
    constructor(capacity) {
        this.capacity = capacity;
    }
    
    sayCapacity() {
        console.log('定員は' + this.capacity + '人です');
    }
}

class Car extends Norimono {
    constructor(capacity, type) {
        super(capacity);
        this.type = type;
    }
    
    sayType() {
        console.log('タイプは' + this.type + 'です');
    }
    
    sayCapacity() {
        console.log(this.type + 'の定員は' + this.capacity + '人だよーん');
    }
}

var car = new Car(4, 'セダン');
car.sayCapacity();

var norimono = new Norimono(100);
norimono.sayCapacity();
セダンの定員は4人だよーん 
定員は100人です

privateメソッド、privateメンバー

SymbolとWeekMapという手法を使った方法があるようです。が、わかりにくく手間がかかる印象…。もう少し理解できてから記事にします。

感想

ES6より前のオブジェクト指向はjavaScript独特で覚えるのも大変でしたが、ES6からは他言語にもよくある文法でわかりやすいです。ただ、ES6から本当のクラスが導入されたわけではなく、仕組みとしてはプロトタイプベースのオブジェクト指向のままだということは理解が必要だと思いました。

あわせて読みたい

タイトルとURLをコピーしました