この記事は技術レビューを必要としています。ぜひご協力ください。
この記事は編集レビューを必要としています。ぜひご協力ください。
JavaScript は、シンプルなオブジェクトベースの枠組みを持つ言語として設計されています。JavaScript におけるオブジェクトはプロパティの集まりからなり、またそのプロパティは名前(あるいはキー)と値との関連付けから成り立っています。プロパティの値を関数にすることもでき、これはいわゆるオブジェクトのメソッドとなります。JavaScript 実行環境に予め定義されているオブジェクトだけでなく、プログラマ自身がオブジェクトを定義することもできます。
本章では、オブジェクト、プロパティ、関数やメソッドの使い方と、オブジェクトを定義する方法を説明します。
オブジェクトの概略
他の多くのプログラミング言語と同様、JavaScript におけるオブジェクトも、現実世界の「もの」(すなわちオブジェクト)になぞらえることができます。JavaScript におけるオブジェクトの概念は、現実世界に実在する「もの」との対比で解釈できます。
JavaScript において、オブジェクトはプロパティと型を持つ独立した存在です。カップを例に考えてみましょう。カップは様々な特性(プロパティ)をもったもの(オブジェクト)です。カップは、色や形状、重さや材質といった性質を持っています。同様に、JavaScript のオブジェクトもプロパティを持つことができ、プロパティによってそのオブジェクトの特徴を定義することができます。
オブジェクトとそのプロパティ
JavaScript のオブジェクトは、自身に関連付けられたプロパティを持ちます。オブジェクトのプロパティは、オブジェクトに関連付けられている変数と捉える事ができます。オブジェクトに属するものという点を除けば、オブジェクトのプロパティは基本的に通常の JavaScript 変数と同じようなものです。オブジェクトのプロパティは、オブジェクトの特性を定義します。以下の様に、オブジェクト名とそのプロパティを単純にドット演算子で繋いで記述する事でオブジェクトのプロパティへアクセスできます。
objectName.propertyName
すべての JavaScript の変数と同じく、オブジェクト名(通常の変数にもなります)とプロパティ名では、大文字と小文字は厳密に区別されます。プロパティに値を代入することでプロパティを定義する事ができます。以下のようにして、myCar という名前のオブジェクトを作成し、make、model、year という名前のプロパティを付与することができます
var myCar = new Object(); myCar.make = "Ford"; myCar.model = "Mustang"; myCar.year = 1969;
JavaScript オブジェクトのプロパティは、ブラケット表記法でもアクセスや設定ができます。オブジェクトは連想配列と呼ばれることがあります。個々のプロパティが、アクセスのために使われる文字列値と関連づけられているからです。ですから例えば、myCar オブジェクトのプロパティに次のようにアクセスできます:
myCar["make"] = "Ford"; myCar["model"] = "Mustang"; myCar["year"] = 1969;
オブジェクトプロパティの名前には、正しい JavaScript 文字列か、空文字列を含む、文字列に変換できるあらゆるものを使えます。しかしながら、JavaScript 識別子として正しくないプロパティ名(例えば空白やダッシュを含んでいたり、数字で始まったりするプロパティ名)には、ブラケット(角括弧)表記法でのみアクセスできます。この表記法はプロパティ名を動的に決める場合(プロパティ名が実行時に決まる場合)に便利です。例を示します:
var myObj = new Object(),
str = "myString",
rand = Math.random(),
obj = new Object();
myObj.type = "Dot syntax";
myObj["date created"] = "String with space";
myObj[str] = "String value";
myObj[rand] = "Random Number";
myObj[obj] = "Object";
myObj[""] = "Even an empty string";
console.log(myObj);
変数内の文字列値を使ってプロパティにアクセスすることもできます :
var propertyName = "make"; myCar[propertyName] = "Ford"; propertyName = "model"; myCar[propertyName] = "Mustang";
for...in でブラケット表記法を使い、オブジェクトの列挙可能なプロパティすべてを巡回することができます。動作説明用の次の関数は、オブジェクトとオブジェクト名を引数として取り、すべてのプロパティを表示します :
function showProps(obj, objName) {
var result = "";
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
result += objName + "." + i + " = " + obj[i] + "\n";
}
}
return result;
}
そして、関数を showProps(myCar, "myCar") のように呼び出すと次の結果が返ります:
myCar.make = Ford myCar.model = Mustang myCar.year = 1969
オブジェクトの全プロパティの列挙
ECMAScript 5 から、オブジェクトのプロパティをリストアップ / トラバース(横断走査)する言語特有の方法が 3 つあります :
for...inループ
このメソッドは、オブジェクトとオブジェクトのプロトタイプチェーンにある列挙可能なプロパティをすべて横断しますObject.keys(o)
このメソッドは、そのオブジェクト独自の(プロトタイプチェーンを除く)、すべての列挙可能なプロパティ名("keys")を、配列で返しますObject.getOwnPropertyNames(o)
このメソッドは、そのオブジェクト独自のすべてのプロパティ名(列挙可能かどうかに関わらず)を配列で返します
ECMAScript 5 には、オブジェクトの全プロパティをリストする言語特有の方法はありません。しかしながら、次の関数で実現できます :
function listAllProperties(o){
var objectToInspect;
var result = [];
for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)){
result = result.concat(Object.getOwnPropertyNames(objectToInspect));
}
return result;
}
これは「隠された」プロパティ(プロパティチェーン上に先に現れる、同名の別のプロパティ)を見つけるのに便利かもしれません。配列内の重複を取り除かないと、アクセス可能なプロパティをリストするのは容易ではありません。
新しいオブジェクトの作成
JavaScript には多くの定義済みオブジェクトがあります。さらに、独自のオブジェクトを定義できます。オブジェクト初期化子を使ってオブジェクトを作れます。もしくは、初めにコンストラクタ関数を作り、その関数と new 演算子を使ってオブジェクトをインスタンス化することもできます。
オブジェクト初期化子の利用
コンストラクタ関数を使ったオブジェクトの作成に加えて、オブジェクト初期化子を使ってオブジェクトを作ることができます。オブジェクト初期化子を使うことを、オブジェクトをリテラル表記法で作ると表現することがあります。「オブジェクト初期化子」は、C++ で使われる用語と同じ意味です。
オブジェクト初期化子を使ったオブジェクト構文は次のようになります :
var obj = { property_1: value_1, // プロパティは識別子だったり、
2: value_2, // 数値だったり
// ...,
"property n": value_n }; // 文字列だったりします
obj は新しいオブジェクトの名前、property_i は識別子(名前、数値、または文字列リテラルのいずれか)、value_i は式で、その値が property_i に代入されます。obj と代入はオプションです。もし他の場所でこのオブジェクトを参照する必要がないのなら、変数への代入は不要です(もし文の期待される場所にオブジェクトを書くなら、リテラルとブロックステートメントが混同されないように、オブジェクトリテラルは括弧で囲む必要があるかもしれません)。
オブジェクト初期化子を使ってオブジェクトがスクリプトのトップレベルで作られると、JavaScript はそのオブジェクトリテラルを含む式を評価するたびにオブジェクトを作ります。さらに、関数内の初期化子は関数が呼ばれるたびに作られます。
次の文では、式 cond が true の場合にのみオブジェクトを作り、変数 x に代入します。
if (cond) var x = {hi: "there"};
次の例では 3 つのプロパティを持った myHonda を作ります。engine プロパティもまた、それ自身のプロパティを持つオブジェクトであることに注意してください。
var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}};
配列を作るのにオブジェクト初期化子を使うこともできます。当ガイドの「配列リテラル」の章をご覧ください。
コンストラクタ関数の利用
別の方法として、次の 2 つの手順でオブジェクトを作ることができます :
- コンストラクタ関数を記述してオブジェクトの型を定義します。この時、便宜上の理由から慣習的に、1 文字目は大文字とします。
newを使ってオブジェクトのインスタンスを作ります。
オブジェクトの型を定義するには、名前、プロパティ、メソッドを指定するオブジェクトの型用の関数を作ります。例えば、車にまつわるオブジェクトの型を作りたいとしましょう。この型の名前は car で、プロパティ make、model、year を持たせたいと思います。これを行うには、以下のような関数を書きます :
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
this を使うことで、関数に渡されたオブジェクトのプロパティに対し値を代入していることに注意してください。
これで、mycar という名前のオブジェクトを次のようにして作ることができます :
var mycar = new Car("Eagle", "Talon TSi", 1993);
この文では mycar を作り、プロパティに指定した値を代入します。mycar.make の値は文字列 "Eagle" になり、mycar.year は整数 1993 になり、といった具合です。
new を使って car オブジェクトをいくつでも作れます。例えば :
var kenscar = new Car("Nissan", "300ZX", 1992);
var vpgscar = new Car("Mazda", "Miata", 1990);
オブジェクトには、別のオブジェクトからなるプロパティを持つことができます。例えば、person という名前のオブジェクトを次のように定義しましょう :
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
それから、2 個の新しい person オブジェクトを次のようにインスタンス化します :
var rand = new Person("Rand McKinnon", 33, "M");
var ken = new Person("Ken Jones", 39, "M");
続けて、person オブジェクトの入る owner プロパティを追加して、car の定義を次のように書き直しましょう :
function Car(make, model, year, owner) {
this.make = make;
this.model = model;
this.year = year;
this.owner = owner;
}
新しいオブジェクトをインスタンス化するため、以下のようにします :
var car1 = new Car("Eagle", "Talon TSi", 1993, rand);
var car2 = new Car("Nissan", "300ZX", 1992, ken);
新しいオブジェクトを作る際、リテラル文字列や整数値を渡す代わりに、上の文ではオブジェクト rand と ken を owner のための引数として渡していることに注意してください。これで、car2 の owner の名前を知りたければ、次のプロパティにアクセスできます :
car2.owner.name
すでに定義されたオブジェクトにはいつでもプロパティを追加できることに注意してください。例えば次の文は
car1.color = "black";
プロパティ color を car1 に追加し、"black" という値をそこに代入します。しかしながら、これは他のどのオブジェクトにも影響しません。同じ型のすべてのオブジェクトに新しいプロパティを追加するには、car オブジェクト型の定義にそのプロパティを追加する必要があります.
Object.create メソッドの利用
オブジェクトは Object.create() メソッドを使っても作れます。コンストラクター関数の定義なしに、作りたいオブジェクトのプロトタイプを選べるため、このメソッドは大変便利です。
// Animal のプロパティとメソッドをカプセル化
var Animal = {
type: "Invertebrates", // プロパティのデフォルト値、「無脊椎動物」
displayType : function(){ // Animal のタイプを表示するメソッド
console.log(this.type);
}
}
// animal1 という新しい animal タイプを作成
var animal1 = Object.create(Animal);
animal1.displayType(); // 出力 : Invertebrates
// Fishes という新しい animal タイプを作成
var fish = Object.create(Animal);
fish.type = "Fishes";
fish.displayType(); // 出力 : Fishes
継承
JavaScript のすべてのオブジェクトは、少なくとも1つの他のオブジェクトを継承しています。継承元になるオブジェクトは prototype として知られ、継承されたプロパティはコンストラクタの prototype オブジェクトにあります。詳細は「継承とプロトタイプチェーン」の章をご覧ください。
オブジェクトプロパティのインデックス付け
オブジェクトのプロパティは、プロパティ名か順序付けられたインデックスで参照することができます。名前によってプロパティを初期定義した場合、常に名前を使って参照する必要があり、インデックスによってプロパティを初期定義した場合、常にインデックスを使って参照する必要があります。
この制限は、オブジェクトとプロパティをコンストラクタ関数を使って作るとき(上で Car オブジェクト型に対してしたように)や、個々のプロパティを明示的に定義した場合(例えば myCar.color = "red")に適用されます。初めにオブジェクトプロパティをインデックスで定義した場合、たとえば myCar[5] = "25 mpg" のようにした場合には、その後はそのプロパティを myCar[5] のようにしないと参照できません。
このルールの例外になるのは 、例えば forms 配列のような HTML から反映されたオブジェクトです。いつでも、この配列にあるオブジェクトはインデックス(文書中に現れる位置基準)と名前(名前が定義されていれば)のどちらからも参照できます。例えば、文書内の2つ目の <FORM> タグが NAME 属性として "myForm" という値を持つとき、フォームは document.forms[1]、document.forms["myForm"] 、document.myForm のいずれかで参照できます。
object の型に対してプロパティを定義する
定義済みのオブジェクト型に対して、prototype プロパティを使ってプロパティを追加できます。このプロパティは 1 つのインスタンスオブジェクトだけではなく、指定された型のすべてのオブジェクトで共有されます。次のコードは color プロパティをすべての car 型のオブジェクトに追加し、オブジェクト car1 の color プロパティに値を代入します。
Car.prototype.color = null; car1.color = "black";
さらなる情報は JavaScript リファレンス内にある、Function オブジェクトの prototype プロパティをご覧ください。
メソッドの定義
メソッドはオブジェクトに関連付けられた関数です。簡単に言えば、オブジェクトのプロパティのうち関数であるものがメソッドです。メソッドは通常の関数と同じ方法で定義されますが、オブジェクトのプロパティに代入される点が異なります。詳細は「メソッドの定義」をご覧ください。例えば :
objectName.methodname = function_name;
var myObj = {
myMethod: function(params) {
// 処理を行う
}
};
ここで objectName は既存のオブジェクトを、methodname はメソッド名にしたい名前を、function_name は関数の名前を指しています。
そして次のようにオブジェクトに続けてメソッドを呼び出します :
object.methodname(params);
オブジェクトのコンストラクタ関数にメソッド定義を含めることで、オブジェクト型のメソッドを定義できます。例えば、定義済みの car オブジェクトのために、プロパティを整形して表示する関数を定義できます。
function displayCar() {
var result = "A Beautiful " + this.year + " " + this.make
+ " " + this.model;
console.log(result);
}
this を使ってメソッドが属するオブジェクトを参照していることに注意してください。
次の文をオブジェクトの定義に追加すると、この関数を car のメソッドにできます。
this.displayCar = displayCar;
最終的には、car の完全な定義は次のようになります。
function Car(make, model, year, owner) {
this.make = make;
this.model = model;
this.year = year;
this.owner = owner;
this.displayCar = displayCar;
}
次のようにして、個々のオブジェクトに対して displayCar メソッドを呼び出せます :
car1.displayCar(); car2.displayCar();
オブジェクトの参照に this を使う
JavaScript は特別なキーワード this は、メソッド内でカレントオブジェクトを参照するのに使います。例えば、 オブジェクトの value プロパティを検証する validate という名前の関数があり、引数にはオブジェクト、最小値、最大値を渡すものとします :
function validate(obj, lowval, hival) {
if ((obj.value < lowval) || (obj.value > hival))
alert("Invalid Value!");
}
これで、各フォーム要素の onchange イベントハンドラから、validate を以下のように呼び出せます :
<input type="text" name="age" size="3" onChange="validate(this, 18, 99)">
通常、メソッド内の this は呼び出し元オブジェクトを参照します。
form プロパティと組み合わせることで、現在のオブジェクトの親フォームを this で参照できます。次の例では、フォーム myForm は Text オブジェクトとボタンを持っています。ユーザーがボタンをクリックすると、Text オブジェクトの値がフォームの名前に設定されます。ボタンの onclick イベントハンドラは親フォームである myForm を参照するのに this.form を使います。
<form name="myForm">
<p><label>Form name:<input type="text" name="text1" value="Beluga"></label>
<p><input name="button1" type="button" value="Show Form Name"
onclick="this.form.text1.value = this.form.name">
</p>
</form>
ゲッターとセッターの定義
ゲッターは特定のプロパティ値を取得するためのメソッドです。セッターは特定のプロパティ値を設定するためのメソッドです。 定義済みのコアオブジェクトや、または新たなプロパティの追加ができるユーザー定義オブジェクトにゲッターとセッターを定義できます。ゲッターとセッターの定義にはオブジェクトリテラル構文を使います。
ユーザー定義オブジェクト o に対し、ゲッターとセッターは以下のように機能します。
var o = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2
}
};
console.log(o.a); // 7
console.log(o.b); // 8
o.c = 50;
console.log(o.a); // 25
o オブジェクトのプロパティは :
o.a— 数値o.b—o.a+ 1 を返すゲッターo.c—o.cに設定される値を 1/2 にしたものをo.aに設定するセッター
[gs]et propertyName(){ } という構文によって誤解を受けるかもしれませんが、オブジェクトリテラル内で "[gs]et property()" によって(あとで紹介する __define[GS]etter__ とは対照的に)定義されたゲッターやセッターの関数名は、ゲッターやセッターそのものの名前ではないことに注意してください。"[gs]et property()" 構文でゲッターやセッターの関数に名前をつけるには、Object.defineProperty(または古い環境用のフォールバックとしての Object.prototype.__defineGetter__)を使って、明示的に名前のついた関数を定義します。
次のコードでは、ゲッターとセッターが Date のプロトタイプを拡張し、すべての定義済み Date クラスのインスタンスに year プロパティを追加する方法を表しています。year プロパティのゲッターとセッターを導入するため、Date クラスの既存の getFullYear と setFullYear メソッドを使います。
次の文で year プロパティのゲッターとセッターを定義します :
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {return this.getFullYear() },
set: function(y) { this.setFullYear(y) }
});
次の文では Date オブジェクトの中でゲッターとセッターを使用しています。
var now = new Date; console.log(now.year); // 2000 now.year = 2001; // 987617605170 console.log(now); // Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001
原則として、ゲッターとセッターは次のどちらの方法でも作れます。
- オブジェクト初期化子による定義
- ゲッターやセッターを追加するメソッドによる、任意のオブジェクトへの後からの追加
オブジェクト初期化子を使ってゲッターとセッターを定義するには、ゲッターメソッドに get プレフィックスを、セッターメソッドに set プレフィックスをつけるだけです。当然ですがゲッターメソッドは引数を想定してはいけませんし、セッターメソッドはただ 1 つの引数(新たな設定値)が想定されます。例えば、 :
var o = {
a: 7,
get b() { return this.a + 1; },
set c(x) { this.a = x / 2; }
};
ゲッターとセッターはまた、メソッド Object.defineProperties を使って、生成済みのオブジェクトにいつでも追加できます。どちらのメソッドも第 1 引数はゲッターまたはセッターに付けたい名前が入ります。第 2 引数はゲッターまたはセッターとして呼ばれる関数です。例を示します(前の例の続きになります):
var o = { a:0 }
Object.defineProperties(o, {
"b": { get: function () { return this.a + 1; } },
"c": { set: function (x) { this.a = x / 2; } }
});
o.c = 10 // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(o.b) // Runs the getter, which yields a + 1 or 6
2つの方法のどちらを選ぶのかは、自身のプログラミングスタイルや行っている作業によります。すでにオブジェクト初期化子を気に入っているのなら、プロトタイプの定義時にほとんどの場合、最初の書式を選ぶでしょう。この書式はより簡潔で自然です。しかしながら、ゲッターやセッターを後から追加しなければならないとき — 自分がプロトタイプやオブジェクトの詳細を書いていない場合 — には、2 番めの書式だけが使えます。2 番目の書式には JavaScript の動的な性質が表現されています — ただし、コードを読みにくく理解しづらいものにする可能性があります。
プロパティの削除
継承されたものでないプロパティは delete 演算子を使って削除できます。次のコードに示すようにプロパティを削除できます :
// 2 つのプロパティ a と b を持つ新しいオブジェクト myobj を作成。 var myobj = new Object; myobj.a = 5; myobj.b = 12; // プロパティ a を削除すると、myobj には b プロパティだけが残る。 delete myobj.a;
delete 演算子はまた、var キーワードを使わずに定義されたグローバル変数の削除にも使えます :
g = 17; delete g;
オブジェクトの比較
JavaScript では、オブジェクトのデータは参照型になっています。2 つの異なるオブジェクトはたとえ同じプロパティを持っていたとしても、等値とは見なされません。自身と同じオブジェクトへの参照が比較された時のみ真となります。
// 2 つの変数、そして同じプロパティを持つ 2 つの異なるオブジェクト
var fruit = {name: "apple"};
var fruitbear = {name: "apple"};
fruit == fruitbear // false が返される
fruit === fruitbear // false が返される
// 2 つの変数、オブジェクトは 1 つ
var fruit = {name: "apple"};
var fruitbear = fruit; // assign fruit object reference to fruitbear に fruit オブジェクトへの参照を代入
// fruit と fruitbear は同じオブジェクトと示される
fruit == fruitbear // true が返される
fruit === fruitbear // true が返される
比較演算子についてのさらなる情報は、リファレンスの「比較演算子」の章をご覧ください。
参照
- より詳しく知りたければ、当ガイドの「オブジェクトモデルの詳細」をお読みください。
- ECMAScript6 のクラス(オブジェクトを作る新しい方法)について知りたければ、リファレンスの「JavaScript クラス」の章をお読みください。