できる気になっているJSを改めてチュートリアルからやってみる 16日目
~~ JavaScriptでの継承、クラス ~~
最近ちょっとずつまた学びなおす必要が出てきたなぁと思い、いろいろプログラムを勉強しなおしているところなんですが、実はもう今使っている知識は古いのかもと思って、アップデートしようとおもいやってみる会。
実施するのは、この記事
完全な初心者向けと書かれたチュートリアルは全然初心者向けではないって話。アップデートしていきましょう。
JavaScript オブジェクト入門 をやってます。
JavaScriptでの継承
OOJS(Object-oriented JavaScriptの略)ライブラリが提供する各種機能のうち、ここでは親クラスからの機能を継承する子オブジェクトクラスの生成方法について解説します。
function Person(first, last, age, gender, interests) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
};
Person.prototype.greeting = function() {
alert('Hi! I\'m ' + this.name.first + '.');
};
こんなかんじで、
・コンストラクタ内部にプロパティを定義
・外部でプロトタイプでメソッドを定義
するようにしました 。ここからTeacherクラスを作成したい場合にどうするかを見ていきます。
Teacher() コンストラクタの機能を定義する
まずコンストラクタを作ります。
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
Personコンストラクタと似ていますが、 call()関数で呼ぶという違いがあります。
最初の引数は関数を実行するときに使用することのできるthisの値を表します。
Teacher() コンストラクタは継承元のPerson()コンストラクタと同じ引数をとりたいため、 call() を呼び出してすべての引数を引数として渡します。コンストラクタの最後の行は、先生が行うべきであり、一般の人が持たない新たな subject のプロパティを定義しています。
引数無しのコンストラクタからの継承
もし継承したコンストラクタがパラメータからプロパティの値を取得しない場合、call()の呼び出しで追加の引数を指定する必要はないです。
function Brick(){
this.width = 10;
this.height = 20;
}
function BlueGlassBrick(){
Brick.call(this);
this.opacity = 0.5;
this.color = 'blue';
}
Teacher()のプロトタイプ とコンストラクタの参照への設定方法
ただ、上記の場合 Personに紐づいたメソッド群(Person.prototype.greeting)は紐づかず使うことができません。Object.getOwnPropertyNames(Teacher.prototype) でみると一つも紐づいていないです。
で、エラーが若干変わる
未定義なのと、名前がないよ(メソッドは存在する)みたいな感じになります。
では、Person() のプロトタイプに定義されたメソッドを継承しつつTeacher() 生成するにはどうするのか?
まずは create() を使うことで、継承をすることができます。
Teacher.prototype = Object.create(Person.prototype);
これによりTeacherの prototype に Person.prototype が作られるので紐づいているという状態にはなる。
また、Teacher.prototype の constructor プロパティは person() と同じになってます。これは、 Teacher.prototype に Person.prototype 自身のプロパティを継承しているオブジェクトへの参照を設定しているためです。
これだと今後問題になる可能性がでそうなので、少しソースコードを変更しましょう。
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;
という感じに書き換えてOK。下の2行を追加はマジックワードになりそう。
Teacher() に greeting() を付け加える
コードを完成させる前に、Teacher() コンストラクタに新たに greeting() 関数を追加する必要がある。
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;
Teacher.prototype.greeting = function () {
var prefix;
if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
prefix = 'Mr.';
} else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
prefix = 'Mrs.';
} else {
prefix = 'Mx.';
}
alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};
みたいな感じで設定する。その後、
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
teacher1.name.first;
teacher1.interests[0];
teacher1.bio();
teacher1.subject;
teacher1.greeting();
teacher1.farewell();
するといい感じに全部動いた。良き良き。
ということでObjectのまとめ(13~16日目)
1. Objectメンバーはオブジェクトインスタンスへ渡されるコンストラクタ関数内部に定義されます。 this.x = x のようなコードです。これ new キーワードでコンストラクタを呼び出されることで生成されます。
→ let myInstance = new myConstructor();
2. Objectメンバーがコンストラクタ自身に直接定義されている場合、コンストラクタでのみ利用可能です。
3. Object メンバーはコンストラクタのプロトタイプに定義されていて、すべてのインスタンスと継承しているオブジェクトのクラスによって継承されます。
ECMAScript 2015のクラス
IEではサポートされていないですが、現在使われているすべてのブラウザでサポートされています。
Classキーワードで今までのものを書き直すと以下
class Person {
constructor(first, last, age, gender, interests){
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
}
greeting() {
console.log(`Hi! I'm ${this.name.first}`);
}
farewell(){
console.log(`${this.name.first} has left the building. Bye for now!`);
}
}
constructorメソッドは、Personクラスを表すコンストラクタ関数を定義します。
greeting() , farewell() はクラスメソッドになります。クラスに関連付けたいメソッドはコンストラクタの後に定義されます。
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
han.greeting();
// Hi! I'm Han
let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
leia.farewell();
// Leia has left the building. Bye for now
という感じ。
で継承は以下。
class Teacher extends Person {
constructor(first, last, age, gender, interests, subject, grade) {
super(first, last, age, gender, interests);
// 科目と学年は教師によって決まっている
this.subject = subject;
this.grade = grade;
}
}
Getter と Setter
class Teacher extends Person {
constructor(first, last, age, gender, interests, subject, grade) {
super(first, last, age, gender, interests);
// 科目と学年は教師によって決まっている
this._subject = subject;
this.grade = grade;
}
get subject() {
return this._subject;
}
set subject(newSubject) {
this._subject = newSubject;
}
}
と設定できます。 subject プロパティの getter と setterは _ を使用して別の値を作成します。この規約を使用しないと get または set を呼び出すたびにエラーが発生します。
・snape オブジェクトの _subject プロパティの現在の値を表示するには、snape.subject getter メソッドを使用します
・_subject プロパティに新しい値を割り当てるには、snape.subject="new value" setter メソッドを使用できます
// デフォルトの値をチェックする
console.log(snape.subject) // Returns "Dark arts"
// 値を変更する
snape.subject="Balloon animals" // Sets _subject to "Balloon animals"
// 新しい値と一致しているか再度チェックする
console.log(snape.subject) // Returns "Balloon animals"
で動作します。以上