jsのプリミティブな型について
JavaScriptのプリミティブな型についての備忘録です。
(今回のテーマ)
以下の違いについて
var text = "hoge"; // 1) String(text); // 2) new String(text);
1) String()
- 戻り値はprimitiveな値
- プロパティが要求されたときは、そのラッパーのオブジェクトのプロパティを返す
- 値はimutableである
2) new String()
- 戻り値はobject
- valueOfメソッドで、そのプリミティブな値を返す
- mutableである(自身を更新可能)
- 状態(独自なプロパティ)を持てる
サンプルコードを書いてみますと、こういう感じです。
// リテラル表記 var a = "hoge"; // String関数で呼び出した場合 var b = String("hoge"); // Stringコンストラクタとして呼び出した場合 var c = new String("hoge"); typeof a; // "string" typeof b; // "string" typeof c; // "object" a["original"] = "foo"; a["original"]; // undefined // "original"はa(primitiveなString)についてのラッパーのプロパティとして呼ばれたので、aに対しては設定されない b["original"] = "foo"; b["original"]; // undefined // aの場合に同じ c["original"] = "foo"; c["original"]; // "foo" // cはobjectなので、任意のプロパティが設定できる // aとbは本質的に同じ var d = new String("hoge"); d == a; // true // 型変換が行われて、どちらもString型になる d === a; // false // 型が異なる b === c; // false // 保持しているプリミティブな値は同じだが、オブジェクトが異なる b == d; // false // オブジェクト同士なので、型変換が行われない a.constructor; // String // aのラッパー(new String(a))のconstructorを返す b.constructor; // String // aに同じ c.constructor; // String // Stringコンストラクタでnewされたので、Stringを返す
といったところですか。
そもそも、重要になってくるのが、プロパティの参照順序です。
自身が指定したプロパティを持っていない場合、参照する順序は以下の順ようになります。
1) 自身がprimitiveな値である場合
自身のラッパのオブジェクトのプロパティを参照する
"hoge".valueOf(); // の場合 // "hoge" // 自身のラッパオブジェクトのプロパティをたどる(存在しないので、さらにそのprototypeをたどる) // (new String("hoge")).valueOf(); と等価 (222).constructor; // Number // 自身のラッパオブジェクトのプロパティをたどる(存在しないので、さらにそのprototypeをたどる) // (new Nunber(222)).constructor; と等価
2) 自身がオブジェクトである場合
({hoge: "foo"}).hoge; // 自身がプロパティを持っている場合、自身のプロパティを返す ({}).toString(); // 自身がプロパティを持っていない場合、そのprototypeのプロパティをたどる // "[object Object]"
この「自身のラッパのオブジェクトのラッパをたどる」挙動は、以下のようなメソッドを作成すると確認できます。
Object.prototype.self = function() { return this; } typeof ("hoge").self(); // "object" ("hoge").self().valueOf(); // "hoge"
たしかに、thisにはobjectが入ってきていますね。
JavaScriptでは、String、Number、Booleanがprimitiveな値として存在します。
比較演算ではこのあたりの挙動を注意しておく必要があるかなーと思いました。