これでできる! クロスブラウザJavaScript入門

第14回 プロトタイプと継承

この記事を読むのに必要な時間:およそ 3 分

ネイティブオブジェクトのprototype

__proto__はほぼすべてのオブジェクトに,prototypeはほとんどの関数に存在します。配列などのネイティブオブジェクトも例外ではありません。

配列のプロトタイプ

var array1 = [1, 2]; // new Array(1, 2)相当
console.log(array1.__proto__ === Array.prototype); // true
console.dir(array1);

図3 Chromeでの実行結果

図3 Chromeでの実行結果

配列array1はArrayコンストラクタのインスタンスであり,array1.__proto__はArray.prototypeと一致します。Pointの例から,Array.prototypeにメソッドを追加する(拡張する)とすべての配列にメソッドが追加される(すべての配列に影響がでる)ことがわかると思います。

配列のプロトタイプ

Array.prototype.insert = function(v, i){
  // i番目にvを挿入
  this.splice(i, 0, v);
  return this;
};
console.log(array1.insert(6, 1)); // [1, 6, 2]

このすべての配列に影響がでるというのは便利でもあり,同時に危険でもあります。この影響で既存のコードが動かなくなるという危険性が生まれるので,安易にネイティブオブジェクトのprototypeを拡張することはお勧めできません。もちろん,問題がないことを確認した上で拡張することを否定しませんし,むしろ個人的にはそういった拡張をよく行ないます。

なお,prototype拡張による具体的な問題点として,for inを用いた際にそのプロパティが列挙されてしまうという問題があります。

配列のfor in

for (var p in array1) {
  console.log(p, ':', array1[p]);
}

配列のfor inの結果

0 : 1
1 : 6
2 : 2
insert : function (v, i){
  // i番目にvを挿入
  this.splice(i, 0, v);
  return this;
}

Array.prototypeに定義したinsertメソッドが見えてしまっています。まず,配列に対してfor inは使わないほうがよいというのも覚えておくべきことですが,ネイティブオブジェクトのprototype拡張はどこかでfor inが使われているんじゃないかと注意しなければいけなくなるのでお勧めできません。

なお,これは先程のpoint1にfor inを用いた時も同様です。

point1のfor in

for (var p in point1) {
  console.log(p, ':', point1[p]);
}

point1のfor inの結果

x : 3
y : 4
length : function (){
  return Math.sqrt(this.x * this.x + this.y * this.y);
}
distance : function (point){
  var dx = this.x - point.x;
  var dy = this.y - point.y;
  return Math.sqrt(dx * dx + dy * dy);
}

こういったオブジェクト自身のプロパティか,__proto__のプロパティか区別がつかない問題に対して,Object.prototype.hasOwnPropertyというメソッドが用意されています。Object.prototypeということは,Objectを継承したオブジェクト(すなわちほぼすべてのオブジェクト)で利用可能なメソッドです。

for inとhasOwnProperty

for (var p in array1) {
  if (array1.hasOwnProperty(p)) {
    console.log(p, ':', array1[p]);
  }
}
for (var p in point1) {
  if (point1.hasOwnProperty(p)) {
    console.log(p, ':', point1[p]);
  }
}

for inとhasOwnPropertyの結果

0 : 1
1 : 6
2 : 2

x : 3
y : 4

ただ,hasOwnPropertyの使い方は覚えておくべきですが,なるべくhasOwnPropertyを使わないで済む実装にしたほうがよいかもしれません。

また,for inを通常は用いることがないであろうString,Number,Functionなどのネイティブオブジェクトであれば,prototypeを拡張しても問題が出にくいため,積極的に拡張してもよいでしょう。

まとめ

今回はプロトタイプベースの継承と,prototypeと__proto__の関係,hasOwnPropertyの使い方などを解説しました。次回はporototypeの継承をより詳しく見ていきます。

著者プロフィール

太田昌吾(おおたしょうご,ハンドルネーム:os0x)

1983年生まれ。JavaScriptをメインに,HTML/CSSにFlashなどのクライアントサイドを得意とするウェブエンジニア。2009年12月より、Google Chrome ExtensionsのAPI Expertとして活動を開始。

URLhttp://d.hatena.ne.jp/os0x/