オブジェクトと型

## アジェンダ
- オブジェクトと型の概要
- 型判定（constructorプロパティ）
- constructorの注意点
- 型判定 (instanceof 演算子と isPrototypeOfメソッド)
- 型判定 (ダックタイピング)
- プロパティの列挙 (プロトタイプ継承を考慮)

## Terms
- constructor (オブジェクトの設計図、雛形的なやつ、最初は大文字 例：Object)
- instance (設計図をもとに生成されたオブジェクト)
- prototype (継承されるプロパティ)

## オブジェクトと型の概要
- オブジェクトの型　＝　object型 (typeOf使うとobjectと出てくる)
- それ以上に細分化はされていない
- オブジェクト指向のプログラミングとしてはオブジェクトの振る舞いに共通性を持たせるのが本質
- ここで言う「振る舞い」がコンストラクタ

## 型判定（constructorプロパティ）
- オブジェクトからconstructorを知るためにオブジェクトのconstructorプロパティが使える。

```js
var d = new Date();
d.constructor;
console.log(d.constructor);
```

```js
var arr = [1, 2, 3];
arr.constructor;
console.log(arr.constructor);
```

```js
var obj = {};
obj.constructor;
console.log(obj.constructor);
```

```js
function object2(a, b) {
  this.a = a;
  this.b = b;
}

var obj2 = new object2();
console.log(obj2.constructor);
```

## constructorの注意点
- constractorプロパティはオブジェクトの直接のプロパティではなく、プロトタイプチェーンで探した先にあるプロパティ

```js
function Derived() {}
function Base() {}
Derived.prototype = new Base(); // DerivedはBaseを拡張継承

var obj = new Derived(); // objはDerivedのinstance
console.log(obj.constructor); // objのコンストラクタはBaseを参照
```

- obj.constructorの実体はDerived.prototype.constructor
- 参照元のBaseのprototypeを間違って変えちゃったりすると危険
- 下記のように再度代入しなおすと大丈夫になる

```js
Derived.prototype.constructor = Derived;
console.log(obj.constructor); // objのコンストラクタはDerivedを参照
```

## 型判定 (instanceof 演算子と isPrototypeOfメソッド)
- オブジェクトの型をconstructorプロパティで判定するのは1つの方法
- 他にも方法がある ＝ instanceof
- これ演算子 (===, !== みたいなやつと同じ使い方)
- 左辺にオブジェクト、右辺にコンストラクタを指定する
- オブジェクトが右辺のコンストラクタで生成されている場合は演算結果が真になる
- プロトタイプチェーンで拡張継承相当の度差をしている場合もinstanceof演算で判定可能
- 下の例だとdはDateのインスタンスだよね?って聞いてる感じ

```js
var d = new Date();
console.log(d instanceof Date);
console.log(d instanceof Object);
```

```js
function Derived() {}
function Base() {}
Derived.prototype = new Base();

var obj = new Derived();
console.log(obj instanceof Derived);
console.log(obj instanceof Base);
console.log(obj instanceof Object);
```

- プロトタイプオブジェクトの確認にはObjectクラスのisPrototypeOfメソッドも使える
- isPrototypeOfメソッドはプロトタイプチェーンをたどる
- 下の例だとobjからプロトタイプをたどってる感じ

```js
console.log(Derived.prototype.isPrototypeOf(obj));
console.log(Base.prototype.isPrototypeOf(obj));
console.log(Object.prototype.isPrototypeOf(obj));
```

## 型判定 (ダックタイピング)
- JSは動的にプロパティが変わることがよくある

```js
var obj = {};
obj.doit = function() {
  console.log('Do it');
}

var obj = {
  doit: function() {
    console.log("Yo! Do it boy! Who's you Daddy!?");
  }
}

obj.doit();
```

- オブジェクトのメソッドの存在はconstructorプロパティやinstanceof演算では判定できない
- オブジェクトにどんなプロパティがあるかを判定する方法が汎用的
- 直接プロパティを見てオブジェクトの振る舞いを判定する方法をダックタイピングと言う
- ダックタイピングに使える1つの方法がin
- 左辺にプロパティ名の文字列、右辺にオブジェクト参照を指定
- オブジェクトが指定プロパティを持つ場合真を返す

```js
var obj = {doit: function() { console.log('Do it!'); }}
console.log('doit' in obj);
console.log('toString' in obj);
```

## プロパティの列挙 (プロトタイプ継承を考慮)
- for in, for each in, inでプロトタイプチェーンをたどれる
- たまに直接のプロパティの存在を判定したいときがある
- そんな時はhasOwnPropertyメソッドを使う

```js
var obj = {x: 1, y: 2, z: 3};

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

- ECMAScript5 ~ のkeysメソッドとgetOwnPropertyNamesメソッドは引数に渡したオブジェクトの直接のプロパティ名の配列を返す
- 実は、すべてのプロパティには属性というものがついてる
- 属性は3タイプあって、writable, enumerable, configurable
- enumerable属性　＝　「数えられる」とかそういう感じ
- 論理属性（trueかfalse）で、enumerableがfalseの属性はfor-in文やObject.keysで列挙されない
- 普通にプロパティに代入したりすると、enumerable属性はtrueのプロパティが作らる
- Object.prototypeが持つメソッドとか、JavaScriptの言語仕様としてすでに存在しているようなプロパティは、基本的に列挙のじゃまにならないようにenumerableがfalseになってるらしい
- そしてkeysメソッドはenumerableが真のもののみ返す
- getOwnPropertyNamesは全部返す
- プロパティのenumerable属性はpropertyIsEnumerableメソッドで判定可能

```js
var obj = {x:1, y:2};
console.log(Object.keys(obj));
console.log(Object.getOwnPropertyNames(obj));
```

```js
var arr = [3,4];
console.log(Object.keys(arr));
console.log(Object.getOwnPropertyNames(arr));
```

```js
console.log(Object.keys(Object.prototype));
console.log(Object.getOwnPropertyNames(Object.prototype));
```

## まとめ

- オブジェクトの振る舞いを確認するのが大事
- constructorの判定にはobj.constructor
- instanceの確認には A instanceof B (AはBのインスタンスだよね?)
- prototypeの確認には B.isPrototypeOf(A) (BはAのプロトタイプだよね？) 
- methodの有無を確認するには 'method' in obj (objにmethodってあるよね？)
- プロパティ全部見たいときは for in obj (objのproperty名を列挙)
- keysは全部列挙、getOwnPropertyNamesは邪魔なのを排除して列挙


*パーフェクトJavaScriptの5章を参照しました。