Otsuka Yuhi
猫に支配されつつあるフロントエンドエンジニアです。スクラムマスターとかもやったりします。ゆめみという会社で働いています。趣味はエレキベースです。スラップ楽しい。
オブジェクト指向プログラミング(オブジェクトしこうプログラミング、英: object-oriented programming, OOP)は、コンピュータ・プログラミングのパラダイムのひとつで、オブジェクト指向の概念や手法を取り入れたものである。プログラムを、データとその振舞が結び付けられたオブジェクトの集まりとして構成する、などといった特徴がある。このパラダイムを指向しているプログラミング言語がオブジェクト指向プログラミング言語である。
wikipedia:オブジェクト指向プログラミングより抜粋
クラスベース方式(継承ベース)
プロトタイプベース方式(インスタンスベース)
mixin方式
JavaScriptはプロトタイプベース方式のオブジェクト指向プログラム
オブジェクトを利用し、データや操作を隠蔽(カプセル化)して、再利用性を高めるプログラミングの手法
コンストラクタ関数 | オブジェクトの設計図の役割を果たす関数(JavaScriptにおける、クラスの本体) |
インスタンス | コンストラクタ関数(クラス)から生成したオブジェクト |
メンバ変数 | インスタンスが保持するデータ |
インスタンスメソッド | インスタンスが保持する関数(メンバ関数) |
プロトタイプメソッド | コンストラクタ関数(クラス)のprototypeプロパティに追加された関数 |
ECMAScript 2015 で導入された JavaScript クラスは、JavaScript にすでにあるプロトタイプベース継承の糖衣構文です。クラス構文は、新しいオブジェクト指向継承モデルを JavaScript に導入しているわけではありません。
MDN:クラスより抜粋
糖衣構文(とういこうぶん、英: syntactic sugar)は、プログラミング言語において、読み書きのしやすさのために導入される書き方であり、複雑でわかりにくい書き方と全く同じ意味になるものを、よりシンプルでわかりやすい書き方で書くことができるもののことである。 by wikipedia
ES5の(擬似的な)クラス
// コンストラクタ関数
function Foo(fuga) {
// var this = {}; // new はこう解釈する
this._fuga = fuga;
this.method = function() {
console.log(this._fuga);
};
// return this; // new はこう解釈する
};
// インスタンス化
var foo = new Foo('foo');
// fooオブジェクトのインスタンスメソッド実行
foo.method(); // -> foo
// foo2オブジェクトのインスタンスを作り、インスタンスメソッドを実行
var foo2 = new Foo('foo2');
foo2.method(); // -> foo2
// foo.method と foo2.methodは、異なるオブジェクトのインスタンスメソッド
console.log(foo.method === foo2.method); // -> false
// コンストラクタ関数
function Foo(fuga) {
// var this = {}; // new はこう解釈する
this._fuga = fuga;
// return this; // new はこう解釈する
};
// プロトタイプに追加
// 関数を作った時点で、prototypeプロパティが生成される
Foo.prototype.method = function() {
console.log(this._fuga);
};
// インスタンス化
var foo = new Foo('foo');
// fooオブジェクトのプロトタイプメソッドを実行
foo.method(); // -> foo
// foo2オブジェクトを作成し、のプロトタイプメソッドを実行
var foo2 = new Foo('foo2');
foo2.method(); // -> foo2
// 同じメソッド
console.log(foo.method === foo2.method); // -> true
// コンストラクタ関数
function Foo(fuga) {
// var this = {}; // new はこう解釈する
this._fuga = fuga;
// return this; // new はこう解釈する
};
// プロトタイプに追加
// 関数を作った時点で、prototypeプロパティが生成される
Foo.prototype.method = function() {
console.log(this._fuga);
};
// インスタンス化
var foo = new Foo('foo');
// fooオブジェクトのプロトタイプメソッドを実行
foo.method(); // -> foo
// foo2オブジェクトを作成し、のプロトタイプメソッドを実行
var foo2 = new Foo('foo2');
foo2.method(); // -> foo2
// 同じメソッド
console.log(foo.method === foo2.method); // -> true
// コンストラクタ関数
function Foo(fuga) {
// var this = {}; // new はこう解釈する
this._fuga = fuga;
this.method = function() {
console.log(this._fuga);
};
// return this; // new はこう解釈する
};
// インスタンス化
var foo = new Foo('foo');
// fooオブジェクトのインスタンスメソッド実行
foo.method(); // -> foo
// foo2オブジェクトのインスタンスを作り、インスタンスメソッドを実行
var foo2 = new Foo('foo2');
foo2.method(); // -> foo2
// foo.method と foo2.methodは、異なるオブジェクトのインスタンスメソッド
console.log(foo.method === foo2.method); // -> false
インスタンスメソッド
プロトタイプメソッド
特別な理由がない限りは、プロトタイプメソッドのパターンがおすすめ
// クラス宣言
class Person {
// コンストラクタ関数
constructor(firstName) {
this._lastName = 'Otsuka';
this._firstName = firstName;
// インスタンスメソッド
this.insMethod = () => {
console.log('[instance]', `私は、${this._firstName} ${this._lastName}です。`);
}
}
// プロトタイプメソッド
method() {
console.log('[prototype]', `私は、${this._firstName} ${this._lastName}です。`);
}
}
// クラス式
// 宣言でも式でも、巻き上がりは起こらない。
// const Person = class {};
// or
// const Person = class Person {};
// クラスは関数。やっていることは、ES5の頃と同じ。
console.log(typeof Person); // -> function
// インスタンス化
const person = new Person('Yuhi');
// インスタンス化することにより、Object になる
console.log(typeof person); // -> object
// プロトタイプメソッドの実行
person.method(); // -> [prototype] 私は、Yuhi Otsukaです。
// インスタンスメソッドの実行(インスタンス毎に作成される)
person.insMethod(); // -> [prototype] 私は、Yuhi Otsukaです。
// クラス宣言
class Person {
// コンストラクタ関数
constructor(firstName) {
this._lastName = 'Otsuka';
this._firstName = firstName;
}
// プロトタイプメソッド
method() {
console.log('[prototype]', `私は、${this._firstName} ${this._lastName}です。`);
}
// 静的メソッド
static staMethod() {
console.log('[static]');
}
}
// インスタンス化するまえでも、実行できる
Person.staMethod(); // -> [static]
// これは駄目
Person.method(); // -> Uncaught ReferenceError: person is not defined
// クラス宣言
class Person {
// コンストラクタ関数
constructor(firstName) {
this._lastName = 'Otsuka';
this._firstName = firstName;
}
// getter
get changeLastName() {
return `婿入りして、${this._firstName} ${this._lastName}になりました。`;
}
// setter
set changeLastName(val) {
this._lastName = val;
}
// プロトタイプメソッド
method() {
console.log('[prototype]', `私は、${this._firstName} ${this._lastName}です。`);
}
}
// インスタンス化
const person = new Person('Yuhi');
// メソッドの実行
person.method();
// -> [prototype] 私はYuhi Otsukaです。
// setter
// _lastNameプロパティを上書き
person.changeLastName = 'Kato';
// プライベート変数の上書きはご法度!
// person._lastName = 'Kato';
// getter
// changeLastNameプロパティに文字列を追加
console.log('[getter]', person.changeLastName);
// -> [getter] 婿入りして、Yuhi Katoになりました。
// クラス宣言
class Person {
// コンストラクタ関数
constructor(firstName) {
this._lastName = 'Otsuka';
this._firstName = firstName;
}
// getter
get changeLastName() {
return `婿入りして、${this._firstName} ${this._lastName}になりました。`;
}
// setter
set changeLastName(val) {
this._lastName = val;
}
// プロトタイプメソッド
method() {
console.log('[prototype]', `私は、${this._firstName} ${this._lastName}です。`);
}
}
// 継承
class Child extends Person {
childMethod() {
// 親のコンストラクタを継承
console.log('[extends]', `私の名前は、${this._firstName}です。`);
// 親メソッドの呼び出し
super.method();
}
}
// 親クラスのコンストラクタ関数を継承し、new してインスタンス化
const child = new Child('Takeshi');
// プロトタイプメソッドの実行
child.childMethod();
// -> [extends] 私の名前は、Otsukaです。
// -> [prototype] 私は、Takeshi Otsukaです。
// 親クラスの get と set を実行
child.changeLastName = 'Kato';
// extends getter
console.log('[extends][getter]', child.changeLastName);
// -> [extends][getter] 婿入りして、Takeshi Katoになりました。
thisと名前を知りたいプログラム
const funcPerson = (firstName, lastName) => {
// それぞれの関数を定義
const sayThis = () => {
console.log(this);
};
const sayFirstName = () => {
console.log(firstName);
};
const sayLastName = () => {
console.log(lastName);
};
const sayFullName = () => {
console.log(firstName, lastName);
};
// 必要なものを実行
sayThis();
sayFirstName();
};
// thisを知りたい and ファーストネームを聞きたい
funcPerson('Yuhi', 'Otsuka');
// -> window{}
// Yuhi
funcPerson('Takeshi', 'Kato');
// -> window{}
// Takesi
// ラストネームを聞きたい
// -> funcPerson関数の sayFirstName(); を sayLastName(); に書き換えて、
// sayThis()をコメントアウトして、実行
// フルネームを聞きたい
// -> funcPerson関数の sayFirstName(); を sayFullName(); に書き換えて、
// sayThis()をコメントアウトして、実行
class Person {
constructor(firstName, lastName) {
this._firstName = firstName;
this._lastName = lastName;
}
// それぞれのメソッドを設定
sayThis() {
console.log(this);
}
sayFirstName() {
console.log(this._firstName);
}
sayLastName() {
console.log(this._lastName);
}
sayFullName() {
console.log(this._firstName, this._lastName);
}
}
const person = new Person('Yuhi', 'Otsuka');
const person2 = new Person('Takeshi', 'Kato');
person.sayThis(); // -> Person{}
// ファーストネームを聞きたい
person.sayFirstName(); // -> Yuhi
person2.sayFirstName(); // -> Takeshi
// ラストネームを聞きたい
person.sayLastName(); // -> Otsuka
person2.sayLastName(); // -> Kato
// フルネームを聞きたい
person.sayFullName(); // -> Yuhi Otsuka
person2.sayFullName(); // -> Takeshi Kato
By Otsuka Yuhi
猫に支配されつつあるフロントエンドエンジニアです。スクラムマスターとかもやったりします。ゆめみという会社で働いています。趣味はエレキベースです。スラップ楽しい。