5章 変数とオブジェクト
変数の宣言
- p109 変数が宣言されていなければ初期値を代入するイディオム
var a = a || 7;
変数と参照
- p110 オブジェクトには名前はなく、オブジェクトを呼び出すための名前をつけるのが変数
- 参照とポインタの違いは、ポインタは演算ができる点
- 変数には、値とオブジェクトの参照が入れられる
- 値はそのものなので、変数aを変数bに代入しても、aとbに相関はない
- オブジェクトの参照が入っている場合は、変数aを変数bに代入すると、参照先が渡されるので、aにアクセスしてプロパティを変更すると、bでのアクセスでもプロパティが変わる
- p111 参照型変数の代入は、参照先のコピーであり、変数aと変数bを同一にするものではない。aをbに代入した後、aに別のオブジェクトを入れた場合は、aとbは違うオブジェクトを参照することになるので、aで変更したプロパティは、bでのアクセスでは別のプロパティ参照になり、影響を受けなくなる
関数の引数(値渡し)
- p112 関数の引数は、値渡し
- 参照型変数でも、参照先の内容の値をコピーするので、引数のもとには影響はない
- p113 複数の結果を返す場合は、配列やオブジェクトを戻り値として返す
文字列と参照
- 文字列型を引数に渡すと、参照で渡すが、利用時に文字列型になるので実質は文字列型として渡したようなもの
- 文字列オブジェクトを代入した変数を渡す場合は参照型で渡す
変数とプロパティ
- p114 JavaScriptでは同じもの
- グローバル変数は、関数外で宣言した変数
- ローカル変数は、関数内で宣言した変数
- トップレベルコードのthisは、グローバルオブジェクトを指している
- プロパティ名 in オブジェクトで、そのオブジェクトにプロパティが含まれているかを確認できる
- p115 クライアントサイドJavaScriptでは、グローバル変数windowが渡される
- 関数が呼び出されると、引数や関数内で宣言される変数が暗黙に生成される。これをCallオブジェクトという
変数名の解決
- トップレベルコードでは、グローバルオブジェクトのプロパティを探す
- ローカル変数は、Callオブジェクトのプロパティ→グローバルオブジェクトのプロパティの順で探す
- 関数が入れ子の場合は、内側から検索して、最後にグローバルオブジェクトのプロパティを探す
変数の存在チェック
- var a=a||7;
- p116 a !== undefined ? a : 7;でチェック
- typeof a !== 'undefined'でチェック
- 'a' in thisでプロパティの有無をチェック
プロパティの存在チェック
- p117 存在しない変数を評価しようとするとエラーが発生するが、存在しないオブジェクトのプロパティはundefinedを返す
- undefinedのプロパティ(未初期化)を利用しようとするとTypeErrorが発生
- TypeErrorの回避方法は以下のイディオムを利用。xがなければundefined、あればyにアクセス
obj.x && obj.x.y
オブジェクトとは
JavaScriptのオブジェクト
- JavaScriptのオブジェクトでは、協調(メッセージング)は、お互いのプロパティを呼び合うことで、共通性(継承)はプロトタイプベースで行う
- オブジェクトとは、データを保持し、その振る舞いをメソッド(関数)でまとめたもの。それらを使ってプログラムを分割して簡潔にしていく
オブジェクトの生成
- オブジェクトの生成は、{}というリテラルで書くとJavaScriptっぽい
- オブジェクトリテラルを使う場面
コンストラクタとnew式
- p122 コンストラクタは普通の関数宣言と同じ形
- newに続けて関数を書くとコンストラクタとして振舞う
- newの評価値は生成されたオブジェクト参照
- newで呼ばれたコンストラクタ内のthisの参照は、新規に作成されたオブジェクトの参照
- newを呼び出すと、空のオブジェクトが生成され、そのオブジェクトをthisとして関数が実行し、式の評価値としてオブジェクト参照が返る
- すべての関数はnewをつけて呼び出すことができ、コンストラクタとして振舞うことができる
- p123 コンストラクタ関数名は、慣例的に大文字から始める
- コンストラクタにreturnをつけると、生成したオブジェクト参照ではなく、returnで指定した戻り値が返ってしまう。コンストラクタにはreturnは書かないこと
プロパティのアクセス
- p124 .か[]でプロパティにアクセス
- .の後ろは識別子。[]内は文字列
- オブジェクトリテラル内では、プロパティは文字列表記でも識別子表記でもどちらでもよい=既存の変数名を書いても、変数の値ではなく、そのまま識別子として扱われる(補足:混乱しない+メモリ節約のため、文字列型にはしないほうがよいだろう)
プロパティ値の更新
- 代入で値設定可能
- 存在しないプロパティを代入先に指定すると、新規に追加される
- deleteでプロパティを削除できる
ドットとブラケットの使い分け
- 通常はドット。以下のいずれかの時はブラケット
- 識別子として使えないプロパティ名を利用するとき
- 変数の値をプロパティ名として利用するとき
- 式の評価結果をプロパティ名に利用するとき
プロパティの列挙
- p126 for inでオブジェクトが持つプロパティを列挙できる。プロトタイプも含む
プロパティの属性
- プロパティには属性がある
- p130 ECMAScript5のプロパティ属性
- writable プロパティ値の書き換えが可能
- enumerable for in文で列挙可能
- configurable 属性を変更可能。削除可能
- get ゲッター関数の指定可能
- set セッター関数の指定可能
- ECMAScript5以前は、enumerable属性のみであり、まだ一般的にはなっていない。利用は普及状況を見ながら
ガベージコレクション
- 使わなくなったオブジェクトを常にdeleteで削除するなどの気配りはしなくてよい。システムに任せる
- 循環参照をすると、メモリリーク(メモリの解放漏れ)が発生するのは気をつける
- パターンがいくつかあるのでツールで検出できる(補足:Googleのleak-finderなど)
不変オブジェクト
- p131 広義には変更しないオブジェクト。狭義には変更をしようにもできないオブジェクト
- JavaScriptでは文字列オブジェクトは不変オブジェクト
不変オブジェクトの有用性
- バグの原因の一つが不正な変数値の書き換え
- これを禁止することでバグのリスクを減らせる
- しかし、不変であることを徹底すると、メモリ効率が悪化するので、利用には兼ね合いが必要
不変オブジェクトの手法
メソッド
- JavaScriptのメソッドは、オブジェクトのプロパティに関数をセットしたもの
- メソッドも関数により実装されているが、オブジェクトに関する手続きを目的としていることを明確にするために呼び分ける
this参照
- p134 どこでも使える読み込み専用の変数
- 処理対象のオブジェクトの参照が入っている変数で、実行する場所や関数の呼び出し方で中身が変化する
this参照の注意点
applyとcall
- f.apply(obj);やf.call(obj);で、thisをobjにして関数fを実行することができる
- p137 applyは、第2引数に、関数に渡す引数を配列で渡す
- callは、第2引数以降に、関数に渡す引数を同じようにコンマ区切りで渡す
プロトタイプ継承
プロトタイプチェーン
- すべての関数(オブジェクト)はprototypeプロパティを持つ(関数名.prototype)
- すべてのオブジェクトは、オブジェクト生成に使ったコンストラクタ関数(関数オブジェクト)のprototypeオブジェクトへの暗黙のリンクを持つ(this.prototypeない。オブジェクトのプロパティにアクセスすると、コンストラクタ関数.prototypeを遡ることができる。オブジェクトはコンストラクタ関数そのものではないので、クラスベースの言語のように、生成元と対応付けてprototypeにアクセスしているということは、暗黙でコンストラクタへのリンクを持っているということになる)
- プロパティの検索順は以下の通り
- プロパティの書き込みでは、prototypeを自動的にさかのぼることはしない
- p139 暗黙リンクの参照先オブジェクトを、プロトタイプオブジェクトと呼び、プロパティ読み込み時に、プロトタイプオブジェクトのプロパティを継承する
- プロトタイプ継承の図
プロトタイプチェーンの具体例
プロトタイプ継承とクラス
プロトタイプチェーンのよくある勘違いと__proto__プロパティ
- 自分自身のオブジェクトのプロパティに見つからなかった後、コンストラクタのプロパティを検索するという間違い。探すのはコンストラクタのprototypeのプロパティ
- オブジェクトのprototypeのプロパティを探すというのは間違い。遡るのはコンストラクタのprototype
- 一部のJavaScriptには__proto__プロパティで、オブジェクトが持っているコンストラクタのprototypeを参照できるものがある。ECMAScriptの仕様ではない
プロトタイプオブジェクト
プロトタイプオブジェクトとECMAScript5
- ECMAScript5から、Object.getPrototypeOf(obj)で、obj.__proto__と同じことができる
オブジェクトと型
- p144 オブジェクトの型は"object"
- どのようにプロパティを構成するかで、実質的な型は決まってくる
型判定(constructorプロパティ)
- obj.constructorで、そのオブジェクトの生成に使ったコンストラクタ関数を参照できる
constructorプロパティの注意点
型判定(ダックタイピング)
- in演算子で、プロパティの有無をチェックすることで、キーとなるプロパティで型を判定するもの
- JavaScriptでは、コンストラクタでオブジェクトが生成されるとは限らず、後付けでプロパティが追加されることもあるので、こちらの方がより実用的
プロパティの列挙(プロトタイプ継承を考慮)
- p147 for inでは、プロトタイプも遡ってすべてのプロパティを列挙する
- 自分自身が持っているプロパティだけをチェックしたい場合は、for inの中で、obj.hasOwnProperty(key)で自分自身のプロパティかを判定して処理する
- ECMAScript5では、keysとgetOwnPropertyNames()が利用できる
- keysは、for-inとhasOwnProperty()で検索したのと同じ内容
- getOwnPropertyNames()は暗黙のプロパティも含めて列挙する
- enumerable属性は、propertyIsEnumerableメソッドでチェックできる
ECMAScript5のObjectクラス
- p148 Object.create()で、プロトタイプオブジェクトを指定してオブジェクトを生成できる
- Object.create(null)とすると、Objectへのプロトタイプを持たないオブジェクトを生成
- prototypeへのオブジェクトの代入などの形にならないので、直感的にかける
プロパティオブジェクト
- p149 createの第2引数には、プロパティと属性をセットにしたオブジェクトを渡す
- 実例がある
アクセッサ属性
その他の型判定
- p152 ECMAScript5には、Array.isArrayメソッドがあるので、配列はこれでチェック
標準オブジェクト
Objectクラス
- p153 Objectを明示してnewするより、オブジェクトリテラルを利用するのが一般的
- ECMAScript5のObjectクラスが持つプロパティ一覧
- p154 Object.prototypeが持つプロパティ一覧
グローバルオブジェクト
- p155 クライアントサイドJavaScriptでは、windowがグローバルオブジェクトに該当する
- ECMAScriptの処理系ではwindowのような名前はない。トップレベルコードのthisでアクセスできる
- p156 サーバサイドJavaScriptなどでは、トップレベルコードでvar global = this;などとして、グローバルオブジェクトを代入しておくなどする
グローバルオブジェクトとグローバル変数
- ECMAScript5のグローバルオブジェクトが持つプロパティ一覧
Mathオブジェクト
- p157 Mathオブジェクトが持つプロパティ一覧
Errorオブジェクト
- エラーのプロパティ一覧
- p158 Error.prototypeの一覧
- JavaScript独自拡張で、fileName、lineNumber、stackなどがある