Javascriptのオブジェクト指向


###JavaScriptにおけるオブジェクト指向の特徴

オブジェクトの複製を作ることをインスタンス化、インスタンス化によってできあがった複 製のことをインスタンスと呼びます。
インスタンス化とは、「オブジェクトを扱うための『自分専用の領域』 を確保する行為」と言い換えても構いません。
オブジェクトをインスタンス化するには new 演算子を利用します。
オブジェクトには、オブジェクトを初期化するために、オブジェクトと同名のメソッドが用意されています。
この初期化メソッドのことをコンストラクターと呼びます。オブジェクト名は、正確にはコンストラクター名です。

コンストラクターとは「インスタンス(オブジェクト)を生成する際に、オブジェクトを初期化する処理を記述するための特殊なメソッド(関数)」のことです。
このMember関数もまた、「new演算子によって呼び出され、オブジェクトを生成する」という意味で、厳密にはクラスというより、コンストラクターと呼ぶのが正しいでしょう。
コンストラクターの名前は、普通の(=コンストラクターでない)関数と区別するために、大文字で始めるのが一般的です。
thisキーワードは、コンストラクターによって生成される インスタンス(つまり、自分自身)を表すものです。

```javascript
var Member = function(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
   this.getName = function() {
     return this.lastName + ' ' + this.firstName;
   }
};
```

####不変Objectを定義する

|メソッド|preventExtensions|seal|freeze|
|:-----------|:------------|:------------|:------------|
|プロパティの追加|不可|不可|不可|
|プロパティの削除|可|不可|不可|
|プロパティ値の変更|可|可|不可|

動的にメソッドを追加できる
同一のクラスを元に生成されたインスタンスであっても、それぞれが持つメンバーは同一であるとは限らない

```javascript
var Member1 = function(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
};

var mem1 = new Member1('A', 'B')
mem1.getName = function(){
   return this.lastName + ' ' + this.firstName;
}

mem1.getName()

//これは動かない
var mem = new Member1('A', 'B');
mem.getName()
```

あとからプロパティを追加、削除できないようにする
sealを追加

```javascript
var Member2 = function(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
   Object.seal(this);
};
var mem2 = new Member2('A', 'B')
mem2.getName = function(){
   return this.lastName + ' ' + this.firstName;
}

mem2.getName()
```

###文脈によって中身が変化する変数 - thisキーワード -

![スクリーンショット 2017-08-24 17.47.53.png](https://qiita-image-store.s3.amazonaws.com/0/39073/a5240ae0-edae-b25e-f304-2b69bdd94003.png "スクリーンショット 2017-08-24 17.47.53.png")

```javascript
var data = 'Global data';
var obj1 = { data: 'obj1 data' };
var obj2 = { data: 'obj2 data' };
function hoge() {
  console.log(this.data);
}
hoge.call(null); //'Global data'
hoge.call(obj1); //'obj1 data'
hoge.call(obj2); //'obj2 data'
```

###コンストラクターの強制的な呼び出し
このコードは問題
thisがグローバルオブジェクトを示すから。

```javascript
var Member = function(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
};
var m = Member('権兵衛', '佐藤');
console.log(m); // 結果:undefined
console.log(firstName); // 結果:権兵衛
console.log(m.firstName); //結果:エラー(Cannot read property 'firstName' of undefined)
```
修正

```javascript
var Member = function(firstName, lastName) {
   if(!(this instanceof Member)) {
     return new Member(firstName, lastName);
   }
   this.firstName = firstName;
   this.lastName = lastName;
};
var m = Member('権兵衛', '佐藤');
console.log(m);//Member {firstName: "権兵衛", lastName: "佐藤"}
console.log(firstName); // firstName is not defined
console.log(m.firstName); // 権兵衛
```

###メソッドはプロトタイプで宣言する - prototypeプロパティ -
そこでJavaScriptでは、オブジェクトにメンバーを追加するために、prototypeというプロパティを用意しています 。
prototypeプロパティは 、デフォルトで空のオブジェクトを参照していますが 、これにプロパティやメソッドを追加することができます。オブジェクトをインスタンス化した場合、インスタンスは元となるオブジェクトに属するprototypeオブジェクトに対して、暗黙的な参照を持つことになります。

メリット
メモリの使用量を節減できる
![スクリーンショット 2017-08-25 18.39.11.png](https://qiita-image-store.s3.amazonaws.com/0/39073/6c90e2a1-8ce5-2404-5d59-2e8e7abe2e83.png "スクリーンショット 2017-08-25 18.39.11.png")

メンバーの追加や変更をインスタンスがリアルタイムに認識できる

![スクリーンショット 2017-08-25 18.41.50.png](https://qiita-image-store.s3.amazonaws.com/0/39073/7495b665-5678-33fe-0044-1026c94f47ab.png "スクリーンショット 2017-08-25 18.41.50.png")

プロトタイプの参照の仕組み

```javascript
var Member = function() { };
Member.prototype.sex = '男';
var mem1 = new Member();
var mem2 = new Member();
console.log(mem1.sex + '|' + mem2.sex);//男|男
mem2.sex = '女';
console.log(mem1.sex + '|' + mem2.sex);//男|女
```
![スクリーンショット 2017-08-25 18.57.47.png](https://qiita-image-store.s3.amazonaws.com/0/39073/c2cac7b6-fe2e-40f7-9f39-2d8b9a40b2c1.png "スクリーンショット 2017-08-25 18.57.47.png")

###オブジェクトリテラルでプロトタイプを定義する
```javascript
var Member = function(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
};
  Member.prototype.getName = function() {
    return this.lastName + ' ' + this.firstName;
};
```

```javascript
var Member = function(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
};
Member.prototype = {
  getName : function() {
    return this.lastName + ' ' + this.firstName;
  }
}
```

###静的プロパティ/ 静的メソッド

```javascript
var Area = function() {}; // コンストラクター
Area.version = '1.0'; // 静的プロパティの定義
// 静的メソッドtriangleの定義
Area.triangle = function(base, height) {
   return base * height / 2;
 };
// 静的メソッドdiamondの定義
Area.diamond = function(width, height) {
   return width * height / 2;
 };
console.log('Areaクラスのバージョン:' + Area.version);
console.log('三角形の面積:' + Area.triangle(5, 3));
var a = new Area()
console.log('菱形の面積:' + a.diamond(10, 2)); // 結果:エラー
```

###プロトタイプチェーン

```javascript
var Animal = function(name) {
  this.name = name;
};
Animal.prototype = {
   walk : function() {
     console.log(name + 'トコトコ');
   }
};

var Dog = function() {
  Animal.call(this, '猫猫');//Animalコンストラクターを現在のthisで呼び出しなさい
};

Dog.prototype = new Animal();
Dog.prototype.bark = function() {
  console.log(this.name + 'ワンワン!');
}
var d = new Dog();
d.walk();
d.bark();
```

Dogオブジェクトのプロトタイプ(Dog.prototype)として、Animalオブジェクトのインスタンスをセットしていることが確認できるはずです。これによって、Dogオブジェクトのインスタンスから、Animal オブジェクトで定義されたwalkメソッドを呼び出せるようになります。

![スクリーンショット 2017-08-25 19.35.48.png](https://qiita-image-store.s3.amazonaws.com/0/39073/da07d9e7-bf56-05a7-cc2e-8a22c41b6ea0.png "スクリーンショット 2017-08-25 19.35.48.png")

```javascript
var Animal = function(name) {
  this.name = name;
};

Animal.prototype = {
  walk : function() {
    console.log(this.name + 'トコトコ');
  }
};

var Dog = function(){
  Animal.call(this, '犬犬');
}
Dog.prototype = new Animal(); // Animal Objectを継承
var d1 = new Dog();
d1.walk();


var SuperAnimal = function(name) {
  this.name = name;
};
SuperAnimal.prototype = {
  walk : function() {
    console.log(this.name + 'ボコボコ');
  }
};
Dog.prototype = new SuperAnimal()//  SuperAnimal Objectを継承
var d2 = new Dog();
d2.walk();//犬犬ボコボコ
d1.walk();//犬犬トコトコ
```
最後の行は「犬犬トコトコ」が表示される。SuperAnimalではなくて、Animalが参照されている。
JavaScriptのプロトタイプチェーンは、インスタンスが生成された時点で固定され、その後の変更にかかわらず保存される

####現在のインスタンスのプロパティだけを列挙する

もしもプロトタイプを参照せずに、現在のインスタンスが持つプロパティだけを列挙したい場合には、以下のように hasOwnProperty メソッドを利用してください。hasOwnProperty メソッドは、指定されたプロパティが現在のインスタンス自身が持つメンバーであるかをtrue / falseで返します

```javascript
for(var key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(key);
  }
}
```

####元となるコンストラクターを取得する - constructorプロパティ -
ただし、プロトタイプ継承している場合、constructor プロパティが示すものは、継承元のクラス(ここではAnimalクラス)

```javascript
var Animal = function() {};
var Hamster = function() {};
Hamster.prototype = new Animal();
var a = new Animal();
var h = new Hamster();
console.log(a.constructor === Animal);//true
console.log(h.constructor === Animal);//true
console.log(h.constructor === Hamster);//false
```

####元となるコンストラクターを判定する - instanceof 演算子 -

```javascript
console.log(h instanceof Animal);//true
console.log(h instanceof Hamster);//true
console.log(a instanceof Hamster);//false
```

####参照しているプロトタイプを確認する - isPrototypeOfメソッド -
```javascript
console.log(Hamster.prototype.isPrototypeOf(h)); // 結果:true
console.log(Animal.prototype.isPrototypeOf(h)); // 結果:true
```

####メンバーの有無を判定する - in演算子 -
```javascript
var obj = { hoge: function(){}, foo: function(){} };
console.log('hoge' in obj); // 結果:true
console.log('piyo' in obj); // 結果:false
```