JavaScriptパターン #3
リテラルとコンストラクタ
オブジェクトの定義
// オブジェクトリテラル記法で定義 var dog = { name: 'Benji', getName: function() { return this.name; } }; // 組み込みのコンストラクタで定義 var dog = new Object(); dog.name = 'Benji'; dog.getName = function() { return this.name; };
リテラルで定義する利点
- オブジェクトは単純で変更可能なハッシュなのでクラスから生成する必要が無い。
- スコープの解決がいらない。コンストラクタを使うと、Object()が呼ばれた場所からグローバルなObjectコンストラクタを見つける必要がある。
→オブジェクトリテラルが使えるのであれば、new Object()を使う理由は特にない。
独自のコンストラクタ関数
var Person = function(name) { this.name = name; this.say = function() { return 'I am ' + name; }; }; var me = new Person('daigo3'); me.say(); // "I am daigo3"
new Person()の様に呼び出すと、関数内部で空のオブジェクトをが作成されてthisで参照できる様になる。コンストラクタは暗黙的にthisを返す(別のオブジェクトを返していない場合)。
※実際にはプロトタイプを継承しているので「空のオブジェクト」ではない
上の例ではthis.sayの様にメソッドを追加しているが、newする度に同じメソッドが生成されてしまうので、プロトタイプに追加する方が良い。→メソッドの様な再利用可能なメンバはプロトタイプで定義する。
Person.prototype.say = function() { return 'I am ' + this.name; };
newを忘れた場合
コンストラクタ関数内のthisがグローバルオブジェクトを参照してしまう。
var Person = function(name) { this.name = name; }; Person.prototype.say = function() { return 'I am' + this.name; }; var p = Person('daigo3'); console.log(p.name); // TypeError: p is undefined console.log(window.name); console.log(name);
ただしECMAScript5では、strictモードの場合はthisはグローバルオブジェクトを指さなくなる。
配列
リテラルを使う方が簡潔。
// リテラル var arr1 = [1, 2, 3]; // 組み込みコンストラクタ var arr2 = new Array(1, 2, 3); console.log(arr1, arr2); //[1, 2, 3] [1, 2, 3] console.log(arr1.constructor === Array); // true console.log(arr2.constructor === Array); // true // コンストラクタに引数を1つだけ渡した場合は // その長さを持つ空の配列になる var arr = new Array(3); console.log(arr.length); // 3 console.log(arr[0]); // undefined // 浮動小数点を長さにしてしてみる var arr = new Array(3.14); // RangeError: invalid array length
配列かどうかのチェック
配列はオブジェクトなのでtypeof演算子を使うと"object"が返ってきてしまう。
var arr = [1, 2 ,3]; console.log(typeof arr === 'object'); // true
var arr = [1, 2, 3]; // instanceofを使う // IEのバージョンによっては、フレームをまたがって使うと正しくチェックできない console.log(arr instanceof Array); // ECMAScript5のArray.isArray()を使う // console.log(Array.isArray(arr)); // Object.prototype.toString()を使う // isArrayが使えない場合 if(typeof Array.isArray === 'undefined') { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; } } console.log(Array.isArray(arr));
JSON
JSONとオブジェクトリテラルの違い
// JSONデータを評価する var jstr = '{ "key": "value"}'; var data = JSON.parse(jstr); console.log(data.key); //"value" // JSONデータにシリアライズする var dog = { name: 'Maru', legs: 4 } var jsonstr = JSON.stringify(dog); //{"name":"Maru","legs":4}
正規表現リテラル
var str = 'abc123XYZ'; // リテラル var no_letters = str.replace(/[a-z]/gi, ''); console.log(no_letters); // 123 // コンストラクタ var letters = str.replace(new RegExp('[0-9]', 'gi'), ''); console.log(letters); //abcXYZ
エラーオブジェクト
name,messageの様なプロパティの他にも、エラーが発生した行番号などを示すプロパティがあるがブラウザ拡張だったりするので、一貫性が無い。
throwでは任意のオブジェクトを指定できるのでカスタムのエラーオブジェクトを作る事ができる。
try { // ... throw { name: 'MyErrorType', message: 'oops', extra: 'extra message', remedy: errorHandler // エラーを処理するハンドラ } } catch (e) { alert(e.message); // oops e.remedy(); // エラーを処理する(errorHandler) }