[JavaScript] そんな継承はイヤだ - クラス定義 - オブジェクト作成

JavaScript のオブジェクト作成においてクラス定義で継承を実装する方法はいくつかあります。
正しい継承はどうあるべきか、基本から検証しながら考えてみたいと思います。

![javascript.jpg](https://qiita-image-store.s3.amazonaws.com/0/16128/c84d931d-8f17-39e9-0789-8fb140732447.jpeg)

※正しくクラス定義がエコ楽にできる様に追加記事書きました。
[[JavaScript] getter/setterも使えるエコ楽なクラス定義 - もちろん継承も - private変数も]
(http://qiita.com/LightSpeedC/items/3946088b58925234cc48)

# 一番簡単なオブジェクトの作成方法

典型的な JavaScript のオブジェクトを簡単に作成してみて、それらを確認してみましょう。

```js
var obj1 = {x: 12, y: "ab"};
var obj2 = new Object;  // または new Object()
obj2.x = 34;
obj2.y = "cd";
// obj < Object

var obj3 = [12, "ab"];
var obj4 = new Array(34, "cd");
// obj < Array < Object
```

![obj-0010-object.png](https://qiita-image-store.s3.amazonaws.com/0/16128/067d9633-83cf-7471-4463-058a09b2986f.png)

## `instanceof` による確認

`instanceof` を使用すると `Object` かどうかがすぐにわかります。

```js
console.log(obj1 instanceof Object);  // -> true
console.log(obj2 instanceof Object);  // -> true
console.log(obj3 instanceof Object);  // -> true
console.log(obj4 instanceof Object);  // -> true
```

`true` なので、全て `Object` の一種です。間違いありません。

## `constructor` による確認

`constructor` を使用すると、オブジェクトが誰によって作成されたのか、つまりどのクラスのオブジェクトなのか確認することができます。

```js
console.log(obj1.constructor === Object);  // -> true
console.log(obj2.constructor === Object);  // -> true
console.log(obj3.constructor === Array);   // -> true
console.log(obj4.constructor === Array);   // -> true
```

後半の例は配列 `Array` です。思った通りですね。

## もう一度 `instanceof` による確認

それでは `Array` なのかどうか `instanceof` で確認してみましょう。

```js
console.log(obj1 instanceof Array);  // -> false
console.log(obj2 instanceof Array);  // -> false
console.log(obj3 instanceof Array);  // -> true
console.log(obj4 instanceof Array);  // -> true
```

後半は配列なので `true` ですが、前半は配列ではありませんので `false` です。予想通りで良かったですね。

## 更に `__proto__` と `prototype` による確認

クラスの `prototype` はどうなっているのか。
それは作成した各オブジェクトの `__proto__` 属性からポイントされています。

```js
console.log(obj1.__proto__ === Object.prototype);  // -> true
console.log(obj2.__proto__ === Object.prototype);  // -> true
console.log(obj3.__proto__ === Array.prototype);   // -> true
console.log(obj4.__proto__ === Array.prototype);   // -> true

// getProto(obj)
var getProto = Object.getPrototypeOf ? Object.getPrototypeOf :
  function getProto(obj) { return obj.__proto__; };

console.log(getProto(obj1) === Object.prototype);  // -> true
console.log(getProto(obj2) === Object.prototype);  // -> true
console.log(getProto(obj3) === Array.prototype);   // -> true
console.log(getProto(obj4) === Array.prototype);   // -> true
```

それぞれのクラスのプロトタイプ・オブジェクトをポイントしているので問題ないですね。

## `__proto__` の `__proto__` による確認

ついでに `__proto__` の `__proto__` も確認しておきましょう。

```js
console.log(obj1.__proto__.__proto__ === null);              // -> true
console.log(obj2.__proto__.__proto__ === null);              // -> true
console.log(obj3.__proto__.__proto__ === Object.prototype);  // -> true
console.log(obj4.__proto__.__proto__ === Object.prototype);  // -> true
console.log(obj3.__proto__.__proto__.__proto__ === null);    // -> true
console.log(obj4.__proto__.__proto__.__proto__ === null);    // -> true

console.log(getProto(getProto(obj1)) === null);              // -> true
console.log(getProto(getProto(obj2)) === null);              // -> true
console.log(getProto(getProto(obj3)) === Object.prototype);  // -> true
console.log(getProto(getProto(obj4)) === Object.prototype);  // -> true
console.log(getProto(getProto(getProto(obj3))) === null);    // -> true
console.log(getProto(getProto(getProto(obj4))) === null);    // -> true
```

`Object` の親クラスは `null` に行き着いておしまい。
`Array` の親クラスは `Object` で、更に親はいない、という事ですね。
この先祖をたぐれる様になっている仕組みがプロトタイプ・チェーンです。

ちなみに `__proto__` は非標準です。
標準では `Object.getPrototypeOf` を使う様に、との事。

![obj-0020-array.png](https://qiita-image-store.s3.amazonaws.com/0/16128/c13b6304-4f02-8a7b-25d6-550a5768e8ac.png)

## 今度は `constructor.name` による確認

`constructor` はオブジェクトを作成する時に使用されたコンストラクタ関数を指しており、関数に名前がある場合 `name` 属性を見れば、その関数名がわかります。

```js
console.log(obj1.constructor.name);  // -> Object
console.log(obj2.constructor.name);  // -> Object
console.log(obj3.constructor.name);  // -> Array
console.log(obj4.constructor.name);  // -> Array
```

正しく表示できましたか。もし思った通りに表示されない場合、以下の互換性対応のおまじないコードを先に実行して、もう一度上記のコードを実行してくれますか。うまくいく事を祈ります。

```js
// 互換性対応のおまじない。主に IE。

var fnameRegExp = /^\s*function\s*\**\s*([^\(\s]*)[\S\s]+$/im;

// fname: get function name
function fname() {
  return ('' + this).replace(fnameRegExp, '$1');
}

// Function.prototype.name
if (!Function.prototype.hasOwnProperty('name')) {
  if (Object.defineProperty)
    Object.defineProperty(Function.prototype, 'name', {get: fname});
  else if (Object.prototype.__defineGetter__)
    Function.prototype.__defineGetter__('name', fname);
}
```

関数の `name` 属性は非標準なので本来は使用してはいけないのですが、デバッグする時に非常に役に立つので上記のコードを実行してから使う事にします。あしからず。

# データだけでなくメソッドも定義してみる

幅(w)と高さ(h)で四角形の面積を計算してみる。

```js
var x1 = {w: 10, h: 20, calc: function () { return this.w * this.h; }};
var x2 = {w: 20, h: 30, calc: function () { return this.w * this.h; }};

console.log(x1.calc());            // -> 200
console.log(x2.calc());            // -> 600
console.log(x1.calc === x2.calc);  // -> false
console.log(x1.calc.toString() === x2.calc.toString());  // -> true
```

メソッドとしての関数を記憶するためのフィールドが、オブジェクトのインスタンスそれぞれに必要となりますね。同じ様に関数定義しても違う関数が登録されている様です。またメソッドの数だけ記憶領域が必要です。あまり効率が良いとは言えませんね。

ちょっとだけマシな定義にするには以下の様にすればいいのでしょうか。

```js
function calc() {
  return this.w * this.h;
}

var x3 = {w: 10, h: 20, calc: calc};
var x4 = {w: 20, h: 30, calc: calc};

console.log(x3.calc());            // -> 200
console.log(x4.calc());            // -> 600
console.log(x3.calc === x4.calc);  // -> true
```

面積をプロパティの様に扱う事も可能です。
その様な場合 getter を使います。

```js
var x5 = {w: 10, h: 20, get area() { return this.w * this.h; }};
var x6 = {w: 20, h: 30, get area() { return this.w * this.h; }};

console.log(x5.area);            // -> 200
console.log(x6.area);            // -> 600
```

ここには例はあげませんでしたが setter も勉強しておきましょう。

こちらに示したサンプルは非常に簡単ですが、本格的にオブジェクトをたくさん作成するには、記憶領域の効率はあまり良くないと思います。

直接オブジェクトを作成するパターンはこれくらいにして本題に進みましょう。

# プロトタイプベースのオブジェクト指向

みなさんはクラスベースのオブジェクト指向言語として Java, C#, C++ なども勉強されているのではないかと思いますが、JavaScript はプロトタイプベースのオブジェクト指向言語なので、ちょっと変わった振る舞いをします。様々なクラス定義の例をみながら検証していきましょう。

## シンプルなクラス定義

JavaScript ではコンストラクタ関数を定義する事で、クラス定義の様に、記述する事が可能です。
まずは `Animal` クラスを定義してみましょう。

```js
// Animal クラス定義
function Animal(name) {
  this.name = name;
}

// Animal クラスのメソッド定義
Animal.prototype.introduce = function introduce() {
  console.log('私は ' + this.constructor.name + ' の ' + this.name + ' です。');
};
```

## インスタンス作成と利用

では `Animal` クラスのインスタンスオブジェクトを作成し、利用してみましょう。

```js
// Animal クラスのインスタンスオブジェクトの作成と利用
var a1 = new Animal('Annie');
a1.introduce();  // -> 私は Animal の Annie です。
```

`this.constructor.name` が使用できるように、上記の様に関数定義としてクラス名を関数に付ける方が良いと思います。

## インスタンスオブジェクトの検証

では、このクラスのインスタンスがちゃんとできているか検証してみましょう。
まずはクラス定義とオブジェクトが正しく作成できているか検証するための関数を作りましょう。

```js
var CSI    = '\u001b[';  // ANSI Control Sequence Introducer
var NORMAL = typeof window !== 'undefined' ? '' : CSI + 'm';
var GREEN  = typeof window !== 'undefined' ? '' : CSI + '32m';
var RED    = typeof window !== 'undefined' ? '' : CSI + '31m';
var YELLOW = typeof window !== 'undefined' ? '' : CSI + '33m';

// getProto(obj)
var getProto = Object.getPrototypeOf ? Object.getPrototypeOf :
  function getProto(obj) { return obj.__proto__; };

// assertTrue: true であればOK、false の時はエラーメッセージを表示する
function assertTrue(bool, msg) {
  if (!bool) {
    console.error(RED + 'Error: ' + msg + NORMAL);
  }
}

// verifyClassObject: オブジェクトの検証
function verifyClassObject(obj, expected, keysExpected) {
  var name       = expected[0];
  var TheClass   = expected[1];
  var SuperClass = expected[2];

  var keys = [];
  for (var i in obj) {
    keys.push(i);
  }
  var keysActual = keys.join(',');
  if (keysActual === keysExpected) {
    console.info(GREEN + 'Success: keys = ' + keysActual + NORMAL);
  }
  else {
    console.error(RED + 'Error: keys = ' + keysActual + ', ' + NORMAL +
      YELLOW + 'Expected: keys ' + keysExpected + NORMAL);
  }

  // obj は Class のインスタンスだ (new Class で作成したからね)
  assertTrue(obj instanceof TheClass,
    name + ' は ' + TheClass.name + ' のインスタンスではない。');

  // obj は SuperClass のインスタンスでもある
  if (SuperClass) {
    assertTrue(obj instanceof SuperClass,
      name + ' は ' + SuperClass.name + ' のインスタンスではない。');
  }

  // obj は Object のインスタンスでもある
  assertTrue(obj instanceof Object,
    name + ' は ' + Object.name + ' のインスタンスではない。');

  // obj のコンストラクタは Class だ
  assertTrue(obj.constructor === TheClass,
    name + ' のコンストラクタは ' + obj.constructor.name + ' で、 ' +
    TheClass.name + ' ではない。');

  // Class のプロトタイプオブジェクトのコンストラクタは Class だ
  assertTrue(TheClass.prototype.constructor === TheClass,
    TheClass.name + ' のプロトタイプは ' + TheClass.prototype.constructor.name + ' で、 ' +
    TheClass.name + ' ではない。');

  // obj の __proto__ を見てみると...
  assertTrue(getProto(obj).constructor === TheClass,
    name + ' の __proto__ は ' + getProto(obj).constructor.name + ' で、 ' +
    TheClass.name + ' ではない。');

  // Class は SuperClass を継承しているんだね
  if (SuperClass) {
    assertTrue(getProto(getProto(obj)) === SuperClass.prototype &&
      getProto(getProto(obj)).constructor === SuperClass &&
      getProto(TheClass.prototype).constructor === SuperClass,
      name + ' の __proto__ の __proto__ は ' +
      getProto(getProto(obj)).constructor.name + ' で、 ' +
      SuperClass.name + ' ではない。');
  }

  // obj の先祖を辿ってみる...
  var expectedString = expected.map(function (fn) {
    return typeof fn === 'function' ? fn.name : fn;
  }).join(' < ');

  var ancestors = [name];
  for (var obj = getProto(obj); obj; obj = getProto(obj)) {
    ancestors.push(obj.constructor.name);
  }

  var actualString = ancestors.join(' < ');
  if (actualString === expectedString) {
    console.info(GREEN + 'Success: ' + actualString + NORMAL);
  }
  else {
    console.error(RED + 'Error: ' + actualString + ', ' + NORMAL +
      YELLOW + 'Expected: ' + expectedString + NORMAL);
  }
  // -> name < Class < SuperClass < Object
}

if (!('info'  in console)) { console.info  = console.log; }
if (!('error' in console)) { console.error = console.log; }
```

## a1 < Animal < Object を検証する

では作成したオブジェクトを検証してみましょう。

```js
// a1 < Animal < Object かどうか検証してみる
verifyClassObject(a1, ['a1', Animal, Object], 'name,introduce');
// -> Success: keys = name,introduce
// -> Success: a1 < Animal < Object
```

もちろん、シンプルな例ですので、特に問題はありませんね。

![obj-0100-animal.png](https://qiita-image-store.s3.amazonaws.com/0/16128/82bfd4e8-c3b2-8f44-8da7-83e492c9f876.png)

## 関数定義には名前を付ける

`a1.constructor.name` で検証ができる様に関数定義には名前を付ける方が良いと考えています。エラー発生時のトレースバック情報にも関数名が含まれますので、そういう場合にも詳しい情報が表示されるので有効です。それはメソッドの関数名も同様です。


もちろん、以下の様な形式でクラス定義してもいいですが、名前が無いクラスを作っているようなものなので、私は推奨はしません。

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

せめて以下の様に名前を付けましょう。

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

## さきほどの簡単なオブジェクトもついでに検証

さきほど作成した簡単なオブジェクト `obj1`～`obj2`, `x1`～`x6` も検証してみましょう。

```js
verifyClassObject(obj1, ['obj1', Object], 'x,y');
// -> Success: keys = x,y
// -> Success: obj1 < Object

verifyClassObject(obj2, ['obj2', Object], 'x,y');
// -> Success: keys = x,y
// -> Success: obj2 < Object

verifyClassObject(obj3, ['obj3', Array, Object], '0,1');
// -> Success: keys = 0,1
// -> Success: obj3 < Array < Object

verifyClassObject(obj4, ['obj4', Array, Object], '0,1');
// -> Success: keys = 0,1
// -> Success: obj4 < Array < Object

verifyClassObject(x1, ['x1', Object], 'w,h,calc');
// -> Success: keys = w,h,calc
// -> Success: x1 < Object

verifyClassObject(x2, ['x2', Object], 'w,h,calc');
// -> Success: keys = w,h,calc
// -> Success: x2 < Object

verifyClassObject(x3, ['x3', Object], 'w,h,calc');
// -> Success: keys = w,h,calc
// -> Success: x3 < Object

verifyClassObject(x4, ['x4', Object], 'w,h,calc');
// -> Success: keys = w,h,calc
// -> Success: x4 < Object

verifyClassObject(x5, ['x5', Object], 'w,h,area');
// -> Success: keys = w,h,area
// -> Success: x5 < Object

verifyClassObject(x6, ['x6', Object], 'w,h,area');
// -> Success: keys = w,h,area
// -> Success: x6 < Object 
```

当然ですが、全て問題ありません。そうなる様に引数をセットしました。

# やっちゃいけない継承、その1

それでは `Animal` クラスを継承して `Bear` クラスを定義してみましょう。

```js
// Bear クラス定義
function Bear(name) {
  Animal.call(this, name);
}

// やっちゃいけない継承、その1
Bear.prototype = Animal.prototype;

// Bear クラスのインスタンスオブジェクトの作成と利用
var b1 = new Bear('Pooh');
b1.introduce();  // -> 私は Animal の Pooh です。

// b1 < Bear < Animal < Object かどうか検証してみる
verifyClassObject(b1, ['b1', Bear, Animal, Object], 'name,introduce');
// -> Success: keys = name,introduce
// -> Error: b1 のコンストラクタは Animal で、 Bear ではない。
// -> Error: Bear のプロトタイプは Animal で、 Bear ではない。
// -> Error: b1 の __proto__ は Animal で、 Bear ではない。
// -> Error: b1 の __proto__ の __proto__ は Object で、 Animal ではない。
// -> Error: b1 < Animal < Object, Expected: b1 < Bear < Animal < Object 
```

なんとなく動いているように見えるんですけど、検証結果はどうでしょうか。

いくつか問題がありそうです。
`b1 < Bear < Animal < Object` っていうのがいいと思うけど、なんか違うね。

では、以下の様な `Bear` クラスのメソッド定義をするとどうなるでしょうか。

```js
Bear.prototype.bearMethod = function bearMethod() {};
```

そうです。`Animal` クラスにも同じ定義ができてしまいますね。
これでは、おかしなことになりそうですね。

もし上記を実行してしまったなら以下で削除しておきましょう。

```js
delete Bear.prototype.bearMethod;
```

![obj-0110-bear.png](https://qiita-image-store.s3.amazonaws.com/0/16128/86eb4586-7f43-1dc9-d8f6-b459615e788c.png)

# やっちゃいけない継承、その2

今度は `Animal` クラスを継承して `Cat` クラスを定義してみましょう。

```js
// Cat クラス定義
function Cat(name) {
  Animal.call(this, name);
}

// やっちゃいけない継承、その2
Cat.prototype = new Animal;

// Cat クラスのインスタンスオブジェクトの作成と利用
var c1 = new Cat('Kitty');
c1.introduce();  // -> 私は Animal の Kitty です。

// c1 < Cat < Animal < Object かどうか検証してみる
verifyClassObject(c1, ['c1', Cat, Animal, Object], 'name,introduce');
// -> Success: keys = name,introduce
// -> Error: c1 のコンストラクタは Animal で、 Cat ではない。
// -> Error: Cat のプロトタイプは Animal で、 Cat ではない。
// -> Error: c1 の __proto__ は Animal で、 Cat ではない。
// -> Error: c1 < Animal < Animal < Object, Expected: c1 < Cat < Animal < Object
```

なんとなく動いているように見えるけど、検証結果はどうでしょうか。

やはり、いくつか問題がありそうです。
`c1 < Cat < Animal < Object` っていうのがいいと思うけど、やっぱりなんか違うね。

# 結果は正しそうだけど互換性が無い継承

今度は `constructor` と `__proto__` を使って `Animal` クラスを継承して `Dog` クラスを定義してみましょう。

```js
// Dog クラス定義
function Dog(name) {
  this.name = name;
}

// 無理やり constructor と __proto__ を使って prototype オブジェクトを上書きする
Dog.prototype = {
  constructor: Dog,
  __proto__: Animal.prototype
};

// Dog クラスのインスタンスオブジェクトの作成と利用
var d1 = new Dog('Hachi');
d1.introduce();  // -> 私は Dog の Hachi です。

// d1 < Dog < Animal < Object かどうか検証してみる
verifyClassObject(d1, ['d1', Dog, Animal, Object], 'name,introduce');
// -> Error: keys = name,constructor,introduce, Expected: keys name,introduce
// -> Success: d1 < Dog < Animal < Object
```

検証結果は正しそうでしたが `for in` で余計な `constructor` が出てきました。
また `__proto__` を使うのは非標準です。

## ところで `ClassName.prototype = {}` って、やっていいの?

なんか、これが問題になっているような気がしますね。
こういう `ClassName.prototype = {}` という記述を見たら間違いと思った方がいいでしょうね。

# 結果は正しいけど互換性が無い継承

今度は `__proto__` だけを使用して `Animal` クラスを継承して `Elephant` クラスを定義してみましょう。

```js
// Elephant クラス定義
function Elephant(name) {
  Animal.call(this, name);
}

// 結果は正しいけど互換性が無い継承
Elephant.prototype.__proto__ = Animal.prototype;

// Elephant クラスのインスタンスオブジェクトの作成と利用
var e1 = new Elephant('Dumbo');
e1.introduce();  // -> 私は Elephant の Dumbo です。

// e1 < Elephant < Animal < Object かどうか検証してみる
verifyClassObject(e1, ['e1', Elephant, Animal, Object], 'name,introduce');
// -> Success: keys = name,introduce
// -> Success: e1 < Elephant < Animal < Object
```

検証結果は正しそうですね。
ですが `__proto__` は標準ではありません。
互換性のないキーワードを使っている事が問題です。

# 正しそうな継承

今度は `Object.create` と `constructor` を使って `Animal` クラスを継承して `Fox` クラスを定義してみましょう。

```js
// Fox クラス定義
function Fox(name) {
  Animal.call(this, name);
}

Fox.prototype = Object.create(Animal.prototype);
Fox.prototype.constructor = Fox;

// Fox クラスのインスタンスオブジェクトの作成と利用
var f1 = new Fox('Gon');
f1.introduce();  // -> 私は Fox の Gon です。

// f1 < Fox < Animal < Object かどうか検証してみる
verifyClassObject(f1, ['f1', Fox, Animal, Object], 'name,introduce');
// -> Error: keys = name,constructor,introduce, Expected: keys name,introduce
// -> Success: f1 < Fox < Animal < Object
```

検証結果は正しそうでしたが `for in` で余計な `constructor` が出てきました。
やはり `enumerable: false` でないとまずいですね。

# 正しい継承

今度は `Animal` クラスを継承して `Gorilla` クラスを定義してみましょう。
継承させるための関数 `inherits` として Node.js の `util.inherits` をそのまま使用してみましょう。

```js
// Gorilla クラス定義
function Gorilla(name) {
  Animal.call(this, name);
}

// console.log(require('util').inherits.toString()); より
function inherits(ctor, superCtor) {
  if (ctor === undefined || ctor === null)
    throw new TypeError('The constructor to `inherits` must not be ' +
                        'null or undefined.');

  if (superCtor === undefined || superCtor === null)
    throw new TypeError('The super constructor to `inherits` must not ' +
                        'be null or undefined.');

  if (superCtor.prototype === undefined)
    throw new TypeError('The super constructor to `inherits` must ' +
                        'have a prototype.');

  ctor.super_ = superCtor;
  Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
//  ctor.prototype = Object.create(superCtor.prototype, {
//    constructor: {
//      value: ctor,
//      enumerable: false,
//      writable: true,
//      configurable: true
//    }
//  });
}

// 正しい継承
inherits(Gorilla, Animal);

// Gorilla クラスのインスタンスオブジェクトの作成と利用
var g1 = new Gorilla('Kong');
g1.introduce();
// -> 私は Gorilla の Kong です。

// g1 < Gorilla < Animal < Object かどうか検証してみる
verifyClassObject(g1, ['g1', Gorilla, Animal, Object], 'name,introduce');
// -> Success: keys = name,introduce
// -> Success: g1 < Gorilla < Animal < Object
```

検証結果も、もちろん定義も問題ありませんね。

![obj-0190-gorilla.png](https://qiita-image-store.s3.amazonaws.com/0/16128/c47e7ee4-8544-2f2e-0cbe-0434d7131bf4.png)

# おまけ1: `new` を付け忘れた時の対応

以下の様に `new` を付け忘れた場合どうなるでしょうか。

```js
try {
  var a2 = Animal('Annie');
} catch (err) {
  console.log(RED + err + NORMAL);
}
```

`this` オブジェクトがグローバルオブジェクトになってしまいますので、以下の様にガードする様にしましょう。

```js
// Animal2 クラス定義
function Animal2(name) {
  if (!(this instanceof Animal2)) {
    return new Animal2(name);
  }
  this.name = name;
}

var a2 = Animal2('Annie');
```

# おまけ2: Closure を使って定義する

以下のコードを見てください。

```js
var a3 = new Animal('Annie');
a3.introduce();        // -> 私は Animal の Annie です。
console.log(a3.name);  // -> Annie
a3.name = 'Aho';
a3.introduce();        // -> 私は Animal の Aho です。
console.log(a3.name);  // -> Aho
```

`name` 属性が外から書き換えられてしまいますね。
Java, C#, C++ 等の言語でいう所の private なフィールドは無いのでしょうか。

Closure を使うとそれに似た事ができます。

```js
// Animal3 クラス定義
function Animal3(name) {
  this.introduce = function introduce() {
    console.log('私は ' + this.constructor.name + ' の ' + name + ' です。');
  };
}

var a3 = new Animal3('Annie');
a3.introduce();        // -> 私は Animal3 の Annie です。
console.log(a3.name);  // -> undefined
a3.name = 'Aho';
a3.introduce();        // -> 私は Animal3 の Annie です。
console.log(a3.name);  // -> Aho
```

それぞれのオブジェクトにメソッド数だけ関数定義が必要になりますが、外部から内部の変数へのアクセスは絶対に不可能になります。公開されたメソッドでないと内部の変数にアクセスすることができないので非常に安全です。

それぞれのオブジェクトに属性を定義していくのか、コンストラクタ関数内の変数を定義し、公開メソッドを定義していくのか、全く違った設計になると思います。

記憶領域の効率は良くないですし、若干遅いのであまりお勧めはしません。

# おまけ3: クラス共通の属性定義

以下の様に `prototype` に共通の属性を定義すると、デフォルト値の様なものが定義できます。それを上書きすることもできます。

```js
// Animal4 クラス定義
function Animal4(name) {
  this.name = name;
  this.animalProp = 123;
}

Animal4.prototype.animalCommonProp = 'abc';

var a4 = new Animal4('Annie');
console.log(a4.animalProp + ' ' + a4.animalCommonProp);
// -> 123 abc
a4.animalProp = 456;
a4.animalCommonProp = 'xyz';
console.log(a4.animalProp + ' ' + a4.animalCommonProp);
// -> 456 xyz
delete a4.animalProp;
delete a4.animalCommonProp;
console.log(a4.animalProp + ' ' + a4.animalCommonProp);
// -> undefined abc
```

最後の行、注意してくださいね。

# 参考文献

## Node.js のリリースノートに正しい継承方法が記述されていました

[v0.8 から v0.10 の主なAPI変更 - Node.js]
(https://github.com/nodejsjp/nodejs.org_ja/wiki/API-changes-between-v0.8-and-v0.10)

`EventEmitter` を継承させる時の注意点として記述があります。

## 型判定

[JavaScriptの「型」の判定について - Qiita]
(http://qiita.com/south37/items/c8d20a069fcbfe4fce85)

`instanceof` を正しく使える様に定義していくべきでしょうね。
型判定には `typeof` が高速なので、まずはそれを使うべきかなぁと思います。

## 重要な事が記述されています

[Javascriptでオブジェクト指向するときに覚えておくべきこと - Qiita]
(http://qiita.com/awakia/items/8ff451ca5f8ae0122be7)

## 以下のような記事を読んで奮起しました。

[【メモ】JavaScriptで継承（っぽいの）- Qiita]
(http://qiita.com/ryokio0129/items/ddf354639889bf4a03e6)

っぽいんですけど、やはり...

[javascriptでの継承の基本パターン４つ - Qiita]
(http://qiita.com/norami_dream/items/ea3827f05699afcb1cc5)

検証してみていない...