Chapter 3. Literals and Constructors
■Object Literal
オブジェクト
// 空のオブジェクトを作る
var dog = {};
//プロパティ、メソッドを追加したり
dog.name = "Benji";
dog.getName = function () {
return dog.name;
};
//プロパティやメソッドを削除したり
delete dog.name;
The Object Literal Syntax
オブジェクトリテラルのルール- {...}で囲う
- 区切りに,を入れる。ただしIEは終端に,があるとエラーにある
- プロパティネーム : プロパティ値
- 最後の}の後に;を入れる
Objects from a Constructor
オブジェクトはリテラル以外にもnew Object()することで作れるがリテラルから作った方がよい。
// リテラルを使った方法
var car = {goes: "far"};
// built-inコンストラクタを使った方法
// warning: this is an antipattern
var car = new Object();
car.goes = "far";
リテラルの方がよい理由としては、リテラルで書いた方が短く書くことができる。また他の理由として、オブジェクトはシンプルで可変なハッシュであり、"レシピ"(クラス)から作られた何かではないということを強調できるからだ。
他には、Objectコンストラクタにたいしてリテラルはスコープ解決の手間がないため早い。
後はbuilt-inコンストラクタを書き換えちゃったりできる場合があるからとか、JavaScriptベストプラクティス30選-jsEdu | Web scratchでもリテラルの方がいいって話書いた。
JavaScript では Object = ... とか Array = ... で書き換えがありますしね。(そんな状況は無いと思いますけど)
Twitter / edvakf: @azu_re あと JavaScript では O ...
Object Constructor Catch
new Object()コンストラクタを使う理由はないから、素直にリテラルを使いましょう。また、Object()コンストラクタから作ったオブジェクトは予想とは違ったコンストラクタプロパティを返すことがあります。
下のような場合はコンストラクタプロパティがObjectなのはいいですが、
// an empty object var o = new Object(); console.log(o.constructor === Object); // truenew Object(引数);として引数をいれてオブジェクトを作ったときに問題が発生します。
下のように予想しないコンストラクタプロパティになることがあり得るため、new Object()コンストラクタを使わないようにしましょう。
// a number object var o = new Object(1); console.log(o.constructor === Number); // true // a boolean object var o = new Object(true); console.log(o.constructor === Boolean); // true
■Custom Constructor Functions
var adam = new Person("Adam");としたときにPersonコンストラクタ関数ではどういうことが起きているか
var Person = function (name) {
// {}を使ってオブジェクトを作る
// var this = {};
// メソッドやプロパティを追加する
this.name = name;
this.say = function () {
return "I am " + this.name;
};
// return this;
};
prototype拡張した時も同じような感じで、thisの部分で拡張済みのオブジェクトが返ってくる
// var this = {} が
// var this = Object.create(Person.prototype); に変わるような感じだ
Constructor’s Return Values
下のような場合、thisを拡張したオブジェクトが返ってくると予測するが、実際にはthatのオブジェクトが返ってくる。
var Objectmaker = function () {
this.name = "This is it";
// creating and returning a new object
var that = {};
that.name = "And that's that";
return that;
};
// test
var o = new Objectmaker();
console.log(o.name); // "And that's that"
これはコンストラクタ関数のreturnで返す値がオブジェクトだったらインスタンスの代わりにそのオブジェクトを返すことになっているからだ。JavaScript の new 演算子の意味: Days on the Moonの下の部分で判定しているのがわかる。
return (result instanceof Object) ? result : instance;
■Patterns for Enforcing new
コンストラクタ関数を実行する時にnewを忘れると大変。thisがグローバル(window)を指してしまって好ましくないし、そもそもエラーになる場合もある。
ECMAScript 5のstrict modeではthisがグローバルオブジェクトを示すことはないらしい。
Naming Convention
前のNaming Conventionsでも言ったけど、コンストラクタ関数は大文字から始めるとわかりやすい。Using that
コンストラクタ関数内でthis使わないで、var that = {};作ってそれを拡張してreturn that;をする方法。returnでオブジェクトを返せばthisを使わなくてもnewしたときにインスタンス化できる。
function Waffle() {
var that = {};
that.tastes = "yummy";
return that;
}
またthatを使わなくてもオブジェクトを返せばいいので、下のようなコンストラクタも可能
function Waffle() {
return {
tastes: "yummy"
};
}
このようにthatを使えば、newを忘れていても同じ結果が返ってくる。
var first = new Waffle(),
second = Waffle();
console.log(first.tastes); // "yummy"
console.log(second.tastes); // "yummy"
だけど、prototypeへのリンクはどこか行ってしまうのが欠点でもある。なので時と場合によって使い分けるか、次のようにする。
Self-Invoking Constructor
new してもしなくても同じインスタンスになるようにするには、コンストラクタ関数の内部で自分自身をnewする方法がある。
function Waffle() {
if (!(this instanceof Waffle)) {
return new Waffle();
}
this.tastes = "yummy";
}
この方法はnewつけないでWaffleを呼んだときは二回Waffleしてしまってるので、下のようにするとよりよいかも。(書籍には書いてなかった)
function Waffle() {
if (this instanceof Waffle) {
this.tastes = "yummy";
}else{
var Traits = function(){}
Traits.prototype = Waffle.prototype;
var obj = new Traits();
Waffle.apply(obj, arguments);
return obj;
}
}
Waffle.prototype.wantAnother = true;
// testing invocations
var first = new Waffle(),
second = Waffle();
console.log(first.tastes); // "yummy"
console.log(second.tastes); // "yummy"
console.log(first.wantAnother); // true
console.log(second.wantAnother); // true
arguments.calleeを使えばコピペで書けるけどstrict modeでは許されないのでハードコーディングしている。■Array Literal
Objectと同じようにArrayも二つの書き方がある。
var a = new Array("itsy", "bitsy", "spider");
// リテラル
var a = ["itsy", "bitsy", "spider"];
console.log(typeof a); // Arrayもまた"object"
console.log(a.constructor === Array); // true
Array Constructor Curiousness
new Arrayにはいくつかのトラップがある。new Array(数値);で数値を入れると、その数だけundefinedで埋められたオブジェクトが返ってくる。
var a = new Array(3); console.log(a.length); // 3 console.log(typeof a[0]); // "undefined"そのため、new Array()に;小数点などを渡すとエラーになる。
var a = new Array(3.14); // RangeError: invalid array length console.log(typeof a); // "undefined"ただ、これにも利用法があり下のように、n-1個分の何かが欲しいときに利用できる。
new Array(256).join(' ');// 255個のホワイトスペース
Check for Array-ness
先ほど言ったようにArrayもまたObjectである。console.log(typeof [1, 2]); // "object"そのためArrayかを判定するためにの方法を探すときに、lengthプロパティをみる方法が浮かぶがlengthはArray.prototype.slice.call(items);など作ったArrayライクなオブジェクトにも存在するため適切ではない。
ECMA5ではArraryを判定する Array.isArray()というメソッドが用意されているが、他の方法として Object.prototype.toString()を利用する方法がある。
if (typeof Array.isArray === "undefined") {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) === "[object Array]";
};
}
Object.prototype.toString()でstringで返ってきたもので判定する。■JSON
JavaScript Object Notationについて。Working with JSON
JSONはevalでも得られるが、セキュリティ的な観点などからJSON.parseを利用する方がよい。ES5ではネイティブでJSONを扱えるが、ネイティブにJSONを扱えない場合はライブラリを利用する。
■Regular Expression Literal
正規表現もコンストラクタとリテラルにより利用できる。Regular Expression Literal Syntax
二つの違いは動的生成new RegExp()と静的生成//。リテラルの場合はパース時に一度だけ正規表現オブジェクトが作られる。
そのためES3の正規表現リテラルでは同じオブジェクトが使い回されるためちょっとバグっぽい動作をすることがある。
function getRE() {
var re = /[a-z]/; // 正規表現オブジェクト
re.foo = "bar";
return re;
}
var reg = getRE(),
re2 = getRE();
console.log(reg === re2); // true => 同じオブジェクトが返ってきてる。
reg.foo = "baz";
console.log(re2.foo); // "baz" <-同じオブジェクトを参照しているため
そのため、ループなどで正規表現リテラルを使うときは気をつける必要がある。この動作はES5では毎回新しいオブジェクトを作るように変更されているため問題ない。
■Primitive Wrappers
JavaScriptではPrimitive型に number, string, boolean, (null, undefined)があるが、この3つをラップしたオブジェクトをprimitive wrapper objectsと呼び、これはbuilt-inコンストラクタである Number(), String(), Boolean()によって作成できる。// a Number object var nobj = new Number(100); console.log(typeof nobj); // "object" primitive wrapper objectが返るこのwrapperオブジェクトは便利なプロパティやメソッド持っているがそれはprimitiveなものでも同じことができる。
■Error Objects
built-inのエラーコンストラクタにはError(), SyntaxError(),TypeError()のようなものがある。これらのオブジェクトはnameとmessageプロパティを持っている。
そして、エラーをthrowするときには任意のものを投げることができるので自分自身でエラーオブジェクトを作成することも可能だ。
try {
// throw an error
throw {
name: "MyErrorType", // custom error type
message: "oops",
extra: "This was rather embarrassing",
remedy: genericErrorHandler // なんかの関数
};
} catch (e) {
alert(e.message); // "oops"
// gracefully handle the error
e.remedy(); // calls genericErrorHandler()
}
throwはオブジェクト以外にもstringやnumberなども投げられる。■Summary
new Object()コンストラクタを使わないでリテラルを使う理由がかなり詳しく書かれていてすばらしい。この著者もthat派だった。(エディタではselfだけじゃなくてthatも色をつけよう)
コメント(0件)
- TB-URL http://efcl.info/adiary/090/tb/