第2章オブジェクト

オブジェクト指向の概要

2010年7月13日

オブジェクト指向の概要

JavaScriptはオブジェクト指向プログラミング(OOP)をサポートしています。オブジェクト指向のオブジェクトは、値(プロパティ)と、それに対する手続き(メソッド)で構成されます。

オブジェクト指向を正確に理解するのは難しいですが、深い理解がなくても初歩的な操作には困りません。ここではオブジェクト指向の詳細は省略し、オブジェクトの初歩的な操作方法、そして用語について重点的に紹介していきます。

オブジェクトの簡単な利用方法

オブジェクトの作成

オブジェクトを作成し、操作する方法を紹介します。
まず、一番シンプルなオブジェクトの作成はブレス( {} )を使った下記になります。

var obj = {};

これで空のオブジェクトが obj に代入されました。

オブジェクトリテラルの表記法

オブジェクトには、自分に属する値、関数を定義することができます。オブジェクト指向ではこのオブジェクトに属する値をプロパティ、関数をメソッドと呼びます。

オブジェクトリテラルとは、ブレス({})で囲まれた、プロパティ名と値のリストです。プロパティ名と値はコロン( : )で区切り、リストはカンマで区切ります。

構文
// オブジェクトのインスタンスを作成
var インスタンス名 = {
// プロパティを定義
プロパティ名 : 値, 
プロパティ名 : 値, 
// メソッドを定義
メソッド名 : function ( 引数1, 引数2, … 引数N ) {
...
}
};

オブジェクトは内部構造を持つデータで、自分に属する値、関数を定義することができます。オブジェクト指向ではこのオブジェクトに属する値をプロパティ、関数をメソッドと呼びます。

プロパティやメソッドを呼び出す際はドット記法を使います。

// プロパティを呼び出す
オブジェクト名.プロパティ名;
// メソッドを呼び出す
オブジェクト名.メソッド名( 引数1, 引数2, … 引数N );

構文にあるように、オブジェクト指向にはインスタンス、プロパティ、メソッドといった専門用語が多くあるので、まずはこれらの名前になれることが大事になります。ここで簡単にそれぞれの名前の意味をリストしておきます。先の章に行って用語の意味がわからなくなった際は、用語集をご利用ください。

オブジェクト指向の用語集

クラス
オブジェクトの設計図のようなもので、プロパティやメソッドを定義したものです。
オブジェクト
クラスやインスタンスなどの総称です。
インスタンス
クラスをもとにnew 演算子で生成したオブジェクトです。
プロパティ
オブジェクトに属する値です。
メソッド
オブジェクトに属する関数です。
コンストラクタ
インスタンス化するときに最初に呼び出されるメソッドです。

プロパティ

プロパティの定義

プロパティはオブジェクトの値です。name と age という2つのプロパティを持ったオブジェクトを定義してみましょう。

var obj = {
name : "ヘルマン・ヘッセ",
age : 27,
};

上記では、name というプロパティは"ヘルマン・ヘッセ"という文字列、age というプロパティは 27 という数値で初期化されています。

識別子として使えない文字、例えばハイフンや空白などがプロパティ名に使われている場合、クォートで囲む必要があります。

var obj = {
"first-name" : "ヘルマン",
};

プロパティの呼び出し

オブジェクトのプロパティにアクセスするには、オブジェクト名とプロパティをドット演算子で繋ぎます。先程作成したオブジェクトの name プロパティにアクセスするには、次のようにします。

console.log( obj.name );

もしくは、次のようにブラケット( [] )を使った連想配列の表記でアクセスすることもできます。

console.log( obj["age"] );

プロパティの名前にハイフンや空白を含めると、ドット記法ではアクセスできません。そのような際は、連想配列の記法を使います。

var obj = {
"first-name" : "ヘルマン",
};
console.log( obj.first-name );
console.log( obj["first-name"] );

出力結果は下記になります。

NaN
ヘルマン

プロパティの追加

プロパティは後から追加することも可能です。

obj.sex = "female";
console.log( obj.sex );

プロパティの削除

プロパティを削除するには、delete演算子を使います。

delete obj.sex;

メソッド

メソッドの定義

プロパティの値が関数のものをメソッドと呼びます。オブジェクトリテラルでメソッドを定義してみましょう。

var obj = {
// メソッドを定義
meth : function ( ) {
console.log("Hello.");
}
};

メソッドの呼び出し

先ほど作成したインスタンスを使ってメソッドを呼び出してみましょう。プロパティと同様にオブジェクト名とメソッド名をドット演算子で繋ぎ、メソッドの場合はさらにメソッド名の後に () を付加し、必要な際は引数を指定します。

obj.meth();
Hello.

今度はコンストラクタを作成し、メソッドに引数を設定してみましょう。

function Hello(VAL) {
// メソッドの定義
this.meth	= function () {
// インスタンス生成時に受けとる引数を設定
console.log(VAL);
}
}
// インスタンス生成時に引数を設定
var obj	= new Hello("Hello!");
// 出力
console.log( obj.meth() );
Hello!

コンストラクタ

コンストラクタの作成

コンストラクタはクラスの呼び出し時に最初に呼び出されるメソッドです。クラスが呼び出されるとコンストラクタが実行され、初期化したオブジェクトの参照を返します。慣習的に、コンストラクタの名前1 文字目は大文字にします。

// コンストラクタの作成
function Person(name) {
this.name = name;
}

プロパティを定義するには、thisキーワードを使って下記のようにします。

this.プロパティ名 

thisキーワードをメソッドの中で使った場合、そのメソッドを格納するオブジェクトを指します。これで、クラスに属するプロパティになります。
それでは、new 演算子を使ってインスタンスを作り、nameプロパティにアクセスしてみましょう。

// Personクラスのインスタンスを作成
var person	= new Person( "ヘルマン・ヘッセ" );
// nameプロパティの値を出力
console.log( person.name );
ヘルマン・ヘッセ

thisを使わずに定義された変数はプロパティとはならず、インスタンスからアクセスできません。

// new 演算子を使ってインスタンスを生成
function Person( name ) {
name = name;
}
// new 演算子を使ってインスタンスを生成
var person	= new Person("ヘルマン・ヘッセ");
// nameはプロパティではないのでアクセスできない
console.log( person.name );
undefined

name はオブジェクトのプロパティなのでアクセスできますが、ageはオブジェクト内の変数のため、アクセスできません。

thisキーワード

thisキーワードは呼び出される状況により何を参照するかが変わります。簡単な考え方としては、オブジェクトに内包されていればそのオブジェクトを参照、内包されていなければwindowオブジェクトを参照します。

// オブジェクトに内包されていないので、thisはwindowオブジェクトを参照します。
console.log( this.location.href );
// オブジェクトに内包されているので、thisはそのオブジェクトを参照します
function Obj(age){
// thisはオブジェクト自身を示し、ageはプロパティとして定義される
this.age = age;
// thisはオブジェクト自身を示し、methはメソッドとして定義される
this.meth = function(){
console.log( this.age );
}
}
// インスタンスの生成
var obj = new Obj(10);
// インスタンスのメソッドを実行
console.log( obj.meth() );

※呼び出し側によるところがあるので、実際はこの通りではないケースもあります。

Object クラス

new 演算子

new 演算子を使ってオブジェクトを生成するには、new 演算子の後に作成したいクラスを指定します。

var obj	= new Object();

上記で指定したObjectクラスはあらかじめ用意されたオブジェクトの中でも特殊なもので、全てのオブジェクトの親にあたります。そのため、全てのオブジェクトは Object クラスに用意された全プロパティと全メソッドを継承し、それらを呼び出して使うことができます。

Objectクラスのインスタンスを生成した場合、何も定義していなくてもObjectクラスが持つプロパティやメソッドを利用できます。
例えば、次の例ではObjectクラスの toString メソッドにアクセスしています。

var obj = new Object();
console.log( obj.toString() );
構文
インスタンス名 = new クラス名();

Objectクラスは下記のようなプロパティ、メソッドを持っています。

Object コンストラクタプロパティ
  • length
  • prototype
Object コンストラクタメソッド
  • assign()
  • create()
  • defineProperty()
  • defineProperties()
  • entries()
  • freeze()
  • getOwnPropertyDescriptor()
  • getOwnPropertyNames()
  • getOwnPropertySymbols()
  • getPrototypeOf()
  • is()
  • isExtensible()
  • isFrozen()
  • isSealed()
  • keys()
  • preventExtensions()
  • seal()
  • setPrototypeOf()
  • values()

コンストラクタプロパティとコンストラクタメソッドは次のように使えます。

console.log( Object.length );
console.log( Object.values("test") );
プロパティ
  • constructor
メソッド
  • hasOwnProperty()
  • isPrototypeOf()
  • propertyIsEnumerable()
  • toSource()
  • toLocaleString()
  • toString()
  • unwatch()
  • valueOf()
  • watch()

Objectがコンテキストで呼び出されたときは、new Object() と同じように振舞います。

console.log( Object().constructor === Object );
console.log( Object().toString() );

インスタンス

new 演算子で生成されたオブジェクトはクラスのインスタンス(実体)と呼ばれます。

var objA	= new Object();
var objB	= new Object();

インスタンスはいくつでも作成可能で、objA、objBはObjectクラスのインスタンスです。objA、objBは同じオブジェクトをもとに生成されますが、値はそれぞれ別に持つことができます。

// objA.ageの値を設定
var objA	= new Object();
objA.age	= 20;
// objB.ageの値を設定
var objB	= new Object();
objB.age	= 40;
// 出力
console.log( "objA.age: " + objA.age );
console.log( "objB.age: " + objB.age );
objA.age: 20
objB.age: 40

インスタンスにプロパティ・メソッドの追加

インスタンス生成後も、プロパティ、メソッドを追加することができます。

// インスタンスを生成し、ageプロパティを設定する
var obj = new Object();
obj.age = 20;
// メソッドを定義
obj.getAge	= function () {
return this.age;
}
// 出力
console.log( obj.getAge() );
20

参照

オブジェクトの代入は値渡しではなく、参照渡しです。参照渡しとは、値のコピーを渡すのではなく、値へのリンクを渡します。

var objA = {
"age" : 20,
}

上記では objA のageプロパティの値として 20 を設定しました。次に、objA を objB に代入し、objBのageプロパティに 40 を代入します。

// objAをobjBに代入
var objB	= objA;
objB.age	= 40;

objBはobjAの値を代入したのではなく、objAの値への参照を代入したので、objBの操作はobjAを操作しているのと同じ意味になります。そのため、上記の処理で objA.age プロパティが 40 になります。
objA と objB の age プロパティを出力してみましょう。

console.log( "objA.age: " + objA.age );
console.log( "objB.age: " + objB.age );

下記のように出力され、objA.age プロパティも変更されていることがわかります。

objA.age: 40
objB.age: 40

配列と関数もオブジェクトの仲間なので、参照渡しになります。

var a	= [1];
var b	= a;
// bに2を代入
b[0]	= 2;
// aを出力
console.log( "a: " + a[0]);
a: 2

上記では、参照先の b に値を代入し、参照元の a 値が変更されたことを確認しています。

全プロパティへのアクセス

for...inループ

オブジェクトの列挙可能なプロパティ全てにアクセスしたい場合、for...in 構文が使えます。

var obj = {
name : "ヘルマン・ヘッセ",
age : 27,
};
for ( var name in obj ) {
console.log(name + " : " + obj[name]);
}
name : ヘルマン・ヘッセ
age : 27

for...in文はプロパティの順序を保証しないので、注意してください。

Object.keys()

keysメソッドは列挙可能なプロパティ名を配列で返します。

Object.keys(obj)
["name", "age"]

プロトタイプ

new演算子を使ってインスタンスを生成する際、元になるオブジェクトがプロトタイプ(雛形)になります。
例えば次の例では、Objectクラスがobjのプロトタイプになります。

var obj = new Object();

これでObjectクラスに属するプロパティやメソッドは全て obj インスタンスに引き継がれます。

prototypeプロパティ

インスタンス生成時の雛形という意味のプロトタイプとは別に、全ての関数オブジェクトは prototype プロパティを持っています。このprototypeプロパティを使って、元のクラスにメソッドやプロパティを追加することができます。

// Stringクラスのインスタンスを生成
var obj = new String("リラックス");
// prototypeを使って log メソッドを追加
String.prototype.log = function () { 
console.log( this.toString() );
}
// 追加したメソッドを実行
obj.log();
リラックス

obj.log()で追加したメソッドが実行されていますが、objインスタンスにlogメソッドが追加されているわけではありません。実際はStringコンストラクタのprototypeにlogメソッドが追加され、それを参照しています。

prototypeプロパティの利点

インスタンスに直接メソッドを追加せず、prototypeプロパティに追加することで、メソッドをインスタンス感で共有することができます。この方法はメモリの節約にもなります。

// log メソッドを追加
String.prototype.log = function () { 
console.log( this.toString() );
}
var obj1 = new String("リラックス");
obj1.log();
var obj2 = new String("ウイン");
obj2.log();
リラックス
ウイン

プロトタイプチェーン

参照されたプロパティをオブジェクトが保持していない場合、プロトタイプのオブジェクトをたどり、該当するプロパティがあれば、そのプロパティが参照されます。そのプロトタイプがプロパティを保持していない場合、そのまたさらにプロトタイプのプロトタイプが探索されます。この仕組みのことをプロトタイプチェーンと呼びます。
最終的に null になるまで探索が続けられ、該当プロパティがなければ undefined が返されます。

// コンストラクタのprototypeにaプロパティを追加
function ObjA() {}
ObjA.prototype.a	= "リラックス";
// ObjBのprototypeにObjAのインスタンスを代入
var ObjB	= function(){}
ObjB.prototype	= new ObjA();
// ObjBのインスタンスを生成
var obj	= new ObjB();
// ObjBのインスタンスからObjAのaプロパティにアクセス
console.log(obj.a);
リラックス

ObjBの prototype に ObjAを代入しているため、ObjBに該当のプロパティがなくても、ObjAのプロパティが探索されています。

Comment

コメントを残す

メールアドレスが公開されることはありません。

関連記事

リズムファクトリーはホームページの制作会社です。
ホームページ制作に関するご要望・ご相談はこちらからどうぞ。

株式会社リズムファクトリーでは現在、下記の職種について人材募集を行っております。
求人をクリックすると「求人情報サイトFind Job!」の求人詳細画面が開きますので、こちらからご応募下さい。

提供 : Webな人の求人情報サイト Find Job!