javascriptの勉強 その2 -オブジェクト-
引き続きjavascriptの勉強を進めていきます。
今回はオブジェクトの話です。
オブジェクト
- javascriptには、単純な変数型として、数値、文字列、真偽値(trueとfalse)、null、undefinedがある
- そして、それ以外の値はすべてオブジェクト
- 数値、文字列、真偽値はメソッドを呼び出すことができる、という意味ではオブジェクトに似ている
- しかし、これらの値はイミュータブル、すなわち一度セットしたら値を変更できない
- 一方、javascriptにおけるオブジェクトはキーによって整理されたミュータブル、つまり変更可能なデータの集合体
- javascriptでは、配列も、関数も、正規表現もすべてオブジェクト
- 当たり前だが、オブジェクトもオブジェクト
- オブジェクトは複数のプロパティをもつコンテナで、名前と値で構成されている
- プロパティ名は任意の文字列が利用でき、空文字も利用可能
- プロパティの値には、undefinedを除くすべてのjavascriptの値をセットできる
- javascriptにおけるオブジェクトはクラスの概念をもたない
- プロパティ名やプロパティの値に関して、何の制約ももたない
- オブジェクトはデータの集約や整理に非常に役立つ
- オブジェクトの中にオブジェクトを含むこともできるので、ツリー構造、グラフ構造を表すことも容易
- javascript には、他のオブジェクトのプロパティを継承することを可能にする、プロトタイプ連鎖の機能が用意されている
- これをうまく利用することで、オブジェクトの初期化のコストとメモリの消費量を抑えることができる
- オブジェクトリテラル
var empty_object = {}; var stooge = { "first-name": "Jerome", "last-name": "Howard" };
- オブジェクトリテラルつづき
- プロパティ名には、空文字を含む任意の文字列が利用できる
- プロパティ名の前後につけるクォート("")は、文字列が予約後ではなく、javascriptの名前に則っていれば、省略することができる
- たとえば、"first-name"の場合はクォートが必要だが、first_nameの場合には省略が可能
- プロパティの区切りにはカンマを利用する
- プロパティの値には任意の式を記述できるし、他のオブジェクトリテラルを含むことも可能(すなわち、オブジェクトはネストすることができる)
var flight = { airline: "Oceanic", number: 815, departure: { IATA: "SYD", time: "2004-09-22 14:55", city: "Sydney" }, arrival: { IATA: "LAX", time: "2004-09-23 10:42", city: "Los Angeles" } };
- 値の取得
- 文字列式を[]で囲んで指定することで、オブジェクトから値を取り出すことができる
- []の代わりに.を利用することもできる(ただし、文字列式が文字列リテラルで、予約語ではないjavascriptの名前のルールに則っている場合に限る)
- .を使った表現の方がおすすめ(コンパクトで読みやすいから)
stooge["first-name"] // "Jerome" flight.departure.IATA // "SYD"
存在しないメンバを取得しようとした場合にはundefinedが返される
stooge["middle-name"] // undefined flight.status // undefined stooge["FIRST-NAME"] // undefined
'||'演算子を使ってデフォルト値を設定することが出来る
var middle = stooge["middle-name"] || "(none)"; var status = flight.status || "unknown";
undefinedに対して、さらにそのプロパティを取得しようとしてしまうと、TypeError例外が投げられてしまう。
&&演算子を利用することでそうした事態を避ける事ができる
flight.equipment // undefined flight.equipment.model // "TypeError"が投げられる flight.equipment && flight.equioment.model // undefined
- 値の更新
- オブジェクトの値は、設定の場合と同様の方法で更新することができる
stooge['first-name'] = 'Jerome'
- プロトタイプ
- すべてのオブジェクトは、プロトタイプオブジェクトとリンクしていて、そこからプロパティを継承している
- オブジェクトリテラルによってつくられたオブジェクトはすべて、javascript標準のObject.prototypeオブジェクトとリンクしている
- オブジェクトを新たに生成する際には、プロトタイプとするオブジェクトを選択することができる
- Object関数にcreateメソッドを追加してみよう
- createメソッドは既存のオブジェクトをプロトタイプとして、新しいオブジェクトを生成する機能をもつ関数
- すべてのオブジェクトは、プロトタイプオブジェクトとリンクしていて、そこからプロパティを継承している
if (typeof Object.create !== 'function'){ Object.create = function (o) { var F = function () {}; F.prototype = o; return new F(); } }
- プロトタイプつづき
- プロトタイプとのリンクはオブジェクトの修正の際には何も影響を及ぼさない。つまり、オブジェクトの内容を書き換えた際に、プロトタイプの内容が変更されることはない。
- プロトタイプとのリンクは、オブジェクトから値を取得する際にのみ利用される
- オブジェクトから指定したプロパティの値を取得する際に、もしそのオブジェクト自身に指定された名前のプロパティがなかった場合、javascriptはプロトタイプオブジェクトからプロパティの値を取得しようとする
- そしてもしそこにもプロパティが存在しなかったら、さらにそのオブジェクトのプロトタイプにさかのぼっていき、最終的にObject.prototypeに行き着くまで検索が行われる
- もし該当するプロパティがプロトタイプチェーン上に存在していなかった場合には、undefinedという値が得られることになる
- この仕組みは「委譲」と呼ばれている
- プロトタイプの結びつきは、動的なものである
- プロトタイプオブジェクトに新しいプロトタイプを追加した場合、そのプロパティはその瞬間から、そのオブジェクトをプロトタイプとするオブジェクトすべてにおいてもアクセス可能になる
stooge.prefession = 'actor'; another_stooge.profession // 'actor'
- プロパティの削除
another_stooge.nickname // 'Moe' // another_stoogeからnicknameプロパティが削除され、 // プロトタイプ上のnicknameプロパティが見えるようになる delete another_stooge.nickname; another_stooge.nickname // 'Curly'
- グローバル領域の利用を減らす
- javascriptにおいて、グローバル変数を定義するのは簡単で、アプリケーションで利用するすべてのデータをそこに格納することができる
- しかしグローバル変数はプログラムの柔軟性を弱めてしまうので、使うべきではない
- 利用するグローバル変数を最低限にする方法の1つは、そのアプリケーションのためにグローバル変数を1つだけ定義することだ