勉強会JS編＜1＞ オブジェクト指向言語としてのJavaScriptを理解


## この記事を読む方へ

* この記事は書籍を基に自分が忘れないように整理したものです。
* 社内の勉強会の資料としても活用しています。
* 詳しい説明が欲しい方は記事の一番下にある書籍を参考にしてください。

## 関連記事

* 勉強会JS編＜1＞ オブジェクト指向言語としてのJavaScriptを理解
* [勉強会JS編＜2＞ クライアントサイドMVCフレームワーク](http://qiita.com/park-jh/items/8b0a21e09223e3a8d0bc)
* [勉強会JS編＜3＞ フロントエンド開発環境構築](http://qiita.com/park-jh/items/319cacc28850c9fd4898)
* [勉強会JS編＜4＞ yeoman + backbone.model + grunt](http://qiita.com/park-jh/items/78bc01b93fe6d4167624)
* [勉強会JS編＜5＞ yeoman + backbone.collection + backbone.localStorage](http://qiita.com/park-jh/items/49fefe30cdf57de7b6f0)
* [勉強会JS編＜6＞ yeoman + backbone.view](http://qiita.com/park-jh/items/44fb4faac997c71201a0)
* [勉強会JS編＜7＞ yeoman + backbone.router](http://qiita.com/park-jh/items/8f7d4275279201d5796b)
* [勉強会JS編＜8＞ yeoman + backbone.js 実践 - その１](http://qiita.com/park-jh/items/bad19e1c6b502ef0d0fa) 
* [勉強会JS編＜9＞ yeoman + backbone.js 実践 - その２](http://qiita.com/park-jh/items/55d62bc58b94b12835c3)

## データタイプ

* 基本型と参照型の違いは「値を変数に格納する方法」である。
* 基本型は値そのものが直接格納される。
* 参照型は参照値（値を実際に格納しているメモリ上のアドレス）が格納される。

### 基本型（プリミティブ型）

1. 数値型（number）
2. 文字列型（string）
3. 真偽型（boolean）
4. 特殊型（null/undefined）
    - null:何か返すべきだが、返すものがない場合に意図的に使われるもの
    - undefined:何もない。定義されていない状態。

### 参照型

1. 配列（array）
    - 各要素にはインデックス番号で接近
2. オブジェクト（object）
    - 各要素には名前で接近
3. 関数（function）
    - 一連の処理の集合

## リテラル

* データ型に格納できる値そのもの
* 数値リテラル（number）
* 文字列リテラル（string）
* 配列リテラル（array）
* オブジェクトリテラル（object）
    - ハッシュ、連想配列
* 関数リテラル（function）
* undefined
    - ある変数の値が定義されていない。
    - 未定義のプロパティを参照しようとするときに返される。
    - Globalオブジェクトでプロパティで定義されているため、値を割当てることもできるが、影響が大きいのでしないように注意する。

## オブジェクト

* 名前のついた値（プロパティ）の集合体、ハッシュ・連想配列と同様。
* 生成方法
    - new + コンストラクタ関数
    - オブジェクトリテラル

## コンストラクタ

* コンストラクタそのものは関数にすぎない。
* コンストラクタ関数をnew演算子を使って呼び出すことでオブジェクトのインスタンスが生成される。
* ネイティブオブジェクトのコンストラクタとカスタムオブジェクトのコンストラクタがある。
* ネイティブオブジェクトのコンストラクタ
    - Number()
    - String()
    - Boolean()
    - Object()
    - Array()
    - Function()
    - Date()
    - RegExp()
    - Error()
* カスタムコンストラクタ
* オブジェクトのインスタンスが生成されるときのコンストラクタ関数内部の動作
    - 空のオブジェクトが作成され、変数thisで参照される。
    - thisはこの関数のプロトタイプを継承する。
    - thisが参照するオブジェクトにプロパティとメソッドが追加される。
    - thisが参照する新しく作られたオブジェクトは、関数の最後で暗黙的に返される。

```javascript
  var Person = function Person(first_name, last_name) {
      // var this = {};
      this.first_name = first_name;
      this.last_name  = last_name;
      this.full_name  = function() {
          return last_name + ' ' + first_name;
      };
      // return this;
  };
```

## 関数

### 関数を定義方法  

* 関数宣言(funtion文)
* Function()コンストラクタ
* 関数リテラル
    - 名前付き関数式：コンストラクタ関数として使われる場合が多い。
    - 名前なし関数式（無名関数）：ある関数の引数として使われる場合が多い。

### 関数宣言（function文）

* 関数宣言はコードが動作する前に解釈される。（関数巻き上げ）
* 関数巻き上げは関数宣言で定義された関数のみ行われる。関数リテラルは例外。
* 引数、return文は省略可能

```javascript
  function 関数名(引数1, 引数2...) {
      実装内容
      return 戻り値;
  }

  ex1)
  var greet = function() {
      say();
      function say() {
          console.log('Hello!');
      };
  }

  ex2)
  console.log(sum(1, 2));
  function sum(x, y) { return x + y; }
```

### Function()コンストラク

* eval(オーバーヘッド、セキュリティ問題)と同様で文字列で定義する形であり避けるべきの使い方である。
* こんな方法もあるという意味で紹介
* 引数、return文は省略可能

```javascript
  var 変数名 = new Function(引数1, 引数2..., 関数の本体)
  ex) var sum = new Function('num1', 'num2', 'return num1 + num2');
```

### 関数リテラル

* 関数を変数に代入する。
* 引数、return文は省略可能

```javascript
  // 名前付き関数式
  var 変数名 = function 関数名(引数1, 引数2...) {
      実装内容
      return 戻り値;
  }

  // 名前なし関数式（無名関数）
  var 変数名 = function(引数1, 引数2...) {
      実装内容
      return 戻り値;
  }
```

### Function()が持つプロパティとメソッド

* プロパティ
    - prototype
* インスタンスプロパティ
    - constructor: オブジェクトを生成したコンストラクタ関数を参照する。
    - arguments: 関数に渡された引数を格納するオブジェクト。
    - length: 関数に設定されているパラメータの数を返す。
* インスタンスメソッド(Function.prototypeのプロパティ)
    - apply(thisArg, argArray): thisArgにthisを渡し、argArrayに渡された配列を引数リストとして関数を実行する。
    - call(thisArg, arg1, arg2...): thisArgにthisを渡し、関数を実行する。
    - toString(): 関数を文字列にして返す。

### 関数の実行方法

* 関数

```javascript
  var foo = function() {
      return 'foo';
  }
  console.log(foo());
```

* メソッド

```javascript
  var foo = {
      bar: function() {
          return 'bar';
      }
  }
  console.log(foo.bar());
```

* コンストラクタ

```javascript
  var Person = function Person(first_name, last_name) {
      this.first_name = first_name;
      this.last_name = last_name;
      this.full_name = function() {
          return last_name + ' ' + first_name;
      };
  };

  var ichiro = new Person('一郎', '鈴木');
  console.log(ichiro.full_name());
```

* apply()とcall()
    - thisを指定する。

```javascript
  var greet = {
      say: function() {
          console.log('Hello, ' + this.name);
      }
  }
  var ichiro = { name: 'ichiro' };
  var hanako = { name: 'hanako' };
  
  greet.say.apply(ichiro);
  greet.say.call(hanako);

  // 同様
  var greet = {
      name: 'ichiro',
      say: function() {
          console.log('Hello, ' + this.name);
      }
  }

  greet.say();
```

## プロトタイプ

### 概念

* あるオブジェクトの元となるオブジェクト
* オブジェクトは別のオブジェクトのプロトタイプとしてできている。

### prototypeプロパティ

* コンストラクタの問題
    - コンストラクタはインスタンスを生成する度に、それぞれのインスタンスのためのメモリを確保する。
    - コンストラクタ内のプロパティはインスタンス毎に異なるが、メソッドは同様なので、メソッドの数に比例して無駄なメモリを消費する。
* コンストラクタの問題を回避するためにはprototypeというプロパティを用意してこれに共通的なプロパティやメソッドを追加できるようにした。
* オブジェクトをインスタンス化した場合、インスタンスは元となるオブジェクトに属するprototypeオブジェクトに対して、暗黙的な参照を持つ。
* インスタンスはプロトタイプへの参照のみ保持する。

```javascript
  // 悪い
  var Person = function Person(first_name, last_name) {
      this.first_name = first_name;
      this.last_name = last_name;
      this.full_name = function() {
          return last_name + ' ' + first_name;
      };
  };

  var ichiro = new Person('一郎', '鈴木');
  console.log(ichiro.full_name());

  // 良い
  var Person = function Person(first_name, last_name) {
      this.first_name = first_name;
      this.last_name = last_name;
  };

  Person.prototype.full_name = function() {
      return this.last_name + ' ' + this.first_name;
  };

  var ichiro = new Person('一郎', '鈴木');
  console.log(ichiro.full_name());

  var hanako = new Person('花子', '鈴木');
  console.log(hanako.full_name());
```

* オブジェクトのメンバが呼び出された時の動作
    - インスタンス側に要求されたメンバが存在しないかを確認する。
    - 存在しない場合は、暗黙の参照をたどってプロトタイプオブジェクトを検索する。

```javascript
  var Person = function Person(first_name, last_name) {
      this.first_name = first_name;
      this.last_name  = last_name;
  };
  Person.prototype.gender = 'male';

  var ichiro = new Person('一郎', '鈴木');
  var hanako = new Person('花子', '鈴木');
  console.log(ichiro.gender, hanako.gender);

  hanako.gender = 'female';
  console.log(ichiro.gender, hanako.gender);
```

* プロパティの宣言はコンストラクタで、メソッドの宣言はプロトタイプで行う。

## 静的プロパティと静的メソッド

* インスタンスを生成せずにオブジェクトから直接呼び出すプロパティとメソッドのこと。
* オブジェクトから直接接近する。インスタンスからは接近できない。

```javascript
  オブジェクト名.プロパティ名 = 値
  オブジェクト名.メソッド名 = function() {}

  Person.area = '東京都';
  console.log(Person.area);
  console.log(ichiro.area);
```

* プロトタイプには適用できない。
    - プロトタイプはあくまでインスタンスから暗黙的に参照されるため使うものである。
* 静的プロパティはコンスタントの用途で使う。
* 静的メソッドの中では、thisキーワードは使えない。
    - インスタンスメソッド内でthisはインスタンス自身を表わす。
    - 静的メソッド内でthisはコンストラクタ（関数オブジェクト）を表わす。
    - インスタンスがないので、静的メソッドからインスタンスプロパティの値に接近するのはできない。
* 静的メンバはグロバル変数・関数と変わらない。
    - ネームスペースと一緒に定義して使うのが好ましい。

## 継承

* プロトタイプチェーン

```javascript
  var Animal = function() {};

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

  var Dog = function() {};
  Dog.prototype = new Animal();
  Dog.prototype.bark = function() { console.log('ワンワン!'); }

  var d = new Dog();
  d.walk();
  d.bark();
```

* 継承関係は動的に変更可能

```javascript
  var Animal = function() {};

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

  var SuperAnimal = function() {};
  SuperAnimal.prototype = {
      walk: function() { console.log('ダダダダダダ!'); }
  };

  var Dog = function() {};
  Dog.prototype = new Animal();
  var d1 = new Dog();
  d1.walk();

  Dog.prototype = new SuperAnimal();
  var d2 = new Dog();
  d2.walk();
  d1.walk();
```

## 参考書籍

* JavaScript本格入門
* 開眼！JavaScript
* JavaScriptパターン