Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

  1. トップページ
  2. JavaScript応用編 - プリミティブとオブジェクトとラッパーオブジェクト

プリミティブとオブジェクトとラッパーオブジェクト

みなさまどうも。

続きましてJavaScriptにおいての『プリミティブ』と『オブジェクト』についてちょっと深いところまで色々とやっていきましょう。これはJavaScriptの内部的な話になりますね。

まず「『プリミティブ』ってなんなん?」ってお話になりますが、プログラムの世界では根本的な値の事を『プリミティブ』と呼んだりします。JavaScriptでは『数値』、『文字列』、『真偽値』、『undefined』、『null』の5つが『プリミティブ』に該当しますね

『プリミティブ』は『プリミティブ値』、『プリミティブ型』、『基本型』、『基本データ型』などなど色々な呼び方で呼ばれますが、著者の体感では『プリミティブ』、または『プリミティブ値』が一般的な気がします。

そしてJavaScriptでは『プリミティブ』に該当しないその他全てのものはオブジェクトっていう扱いになってます。

『オブジェクト』は直訳すると「物」になるのですが、「物」だとイメージしにくいので「データや処理(プロパティやメソッドなど)が色々集まって構成されたもの」ってイメージするようにすると良いかもしれません。

さて、ではJavaScriptにおける『プリミティブ』と『オブジェクト』の動作の違いについて確認していきましょう。

まずJavaScriptで『オブジェクト』のプロパティやメソッドにアクセスするには『.』、または『[]』を使用しますよね。こんな感じです。

var onePunchMan = {
    WrittenBy: "ONE",
    IllustratedBy: "村田雄介"
};

console.log(onePunchMan.WrittenBy); // 文字列『ONE』が出力されます。
console.log(onePunchMan["IllustratedBy"]); // 文字列『村田雄介』が出力されます。

(´-`).。oO(著者が現在ハマっている『ワンパンマン』の内容をお借りしてます...)

(´-`).。oO(めっちゃ面白いのでご興味ある方はぜひ...)

JavaScriptでの関数(メソッド)は『関数オブジェクト』という『オブジェクト』なのでプロパティを持たせることができます。

var onePunchMan = function(){ // 関数定義します。
};

onePunchMan.WrittenBy = "ONE"; // 関数はプロパティを持てます。
onePunchMan.IllustratedBy = "村田雄介";
onePunchMan.PublishedBy = "集英社";

んでもって『プリミティブ』は根本的な値なのでプロパティを持てません。なので以下の記述はエラーになります。

null.test = "hoge"; // 『null』は『プリミティブ』なのでプロパティを持てません。

こんな感じでプロパティを持てるのは『オブジェクト』のみとなっています。これが『プリミティブ』と『オブジェクト』の大きな違いです。

しかし、ちょっと思い出して頂きたいのですが文字列の文字数を数えられる『length』ってプロパティありましたよね。使い方はこんな感じでした。

var s = "onePunchMan";
console.log(s.length); // 数値『11』が出力されます。

JavaScriptでの『文字列』は『プリミティブ』なのになぜか『length』ってプロパティを持っているようです。なんだか変な感じですね。

その秘密はとある一時的なオブジェクトでラップされてるせいだったりします。

JavaScriptで文字列を定義する方法として『"』や『'』を使用する以外に『new String()』を使用する方法があったりします。

var s = new String("onePunchMan"); // こういう記述でも文字列を定義できます。

この『String()』というのは文字列用にJavaScript側で元々用意されている関数(メソッド)です。これで生成された文字列は普通の文字列とはちょっと違い、『オブジェクト』の文字列となります。

ちょっとグーグルクロームの『console.log()』で中身を覗いて差をみてみましょう。まずは通常の文字列を定義した場合です。

var s = "onePunchMan";
console.log(s);

これを実行させるとこうなります。

onePunchMan

ただ『onePunchMan』と出力されてるだけですね。

続いて『new String()』で定義してみます。

var s = new String("onePunchMan");
console.log(s);

これを実行させるとこうなります。

String {0: "o", 1: "n", 2: "e", 3: "P", 4: "u", 5: "n", 6: "c", 7: "h", 8: "M", 9: "a", 10: "n", length: 11, [[PrimitiveValue]]: "onePunchMan"}

さっきと違ってなんだかいっぱいプロパティとかが詰まってますね。これが通常の文字列と『オブジェクト』の文字列の違いです。

さらにこの中身をみてみると『length』ってプロパティがいますね。なんだかこいつが怪しいです。

というわけでタネ明かしをすると、通常の文字列を操作しようとしたときはJavaScriptが裏側でその文字列を『String()』の機能を持った一時的なオブジェクトでラップさせた後にアクセスさせていたりします。存在するはずのない『length』などのプロパティが参照できてしまう秘密はこれです。

そして、この一時的にラップするために生成されるオブジェクトは『ラッパーオブジェクト』と呼ばれます。まあこの言葉は特に覚えなくても良い気がしてます。たまに参考書に出てくる程度の使用頻度です。

ちなみに数値用や真偽値用の『ラッパーオブジェクト』も存在しており、アクセスするときは裏側で勝手にラップされたりしています。

ただし、動作がそれぞれちょっと違ったりするので注意して下さい。

// 色々と定義します。
var n = new Number(1), // Numberメソッドを使って数値を定義します。
    _n = 1, // 数値を定義します。
    b = new Boolean(true), // Booleanメソッドを使って真偽値を定義します。
    _b = true, // trueを定義します。
    s = new String("onePunchMan"), // Stringメソッドを使って文字列を定義します。
    _s = "onePunchMan"; // 文字列を定義します。

// 数値の場合。
n.test; // これはOKです。
_n.test; // 変数に代入した数値ならばラッパーオブジェクトでラップしてくれるのでこれはOKです。
0.test; // そのままの数値のプロパティに直接アクセスしようとするとエラーになります。

// 真偽値の場合。
b.test; // これはOKです。
_b.test; // これはOKです。
true.test; // これはOKです。

// 文字列の場合。
s.test; // これはOKです。
_s.test; // これはOKです。
"onePunchMan".test; // これはOKです。

『0.test;』ってやつに注目してみましょう。数値の場合のみ直接プロパティにアクセスしようすると怒られちゃっていますね。注意です。

んでもって『null』と『undefined』の『ラッパーオブジェクト』は存在しませんので以下の構文はエラーになります。

null.test; // エラーです。
undefined.test; // エラーです。

さて、この『ラッパーオブジェクト』というオブジェクトですが、一時的なオブジェクトになりますので『ラッパーオブジェクト』のプロパティを書き換えようとしても意味はありません。

var s = "onePunchMan";
console.log(s.test); // 『undefined』が出力されます。

s.test = 0; // 『s.test』に数値『0』を代入します。

console.log(s.test); // アクセスする度に新しい『ラッパーオブジェクト』が生成されるため、変わらず『undefined』が出力されます。

『new String()』などを使用して文字列等を定義した場合は『オブジェクト』の扱いとなり『ラッパーオブジェクト』でラップされているわけではないためプロパティが保持できちゃったりします。ちょっとややこしいですが間違えないようにして下さい。

var s = new String("onePunchMan"); // 『new String()』を使用して定義します。
console.log(s.test); // 『undefined』が出力されます。

s.test = 0; // 『s.test』に数値『0』を代入します。

console.log(s.test); // こちらは数値『0』が出力されます。

『new String()』などを使用せずに定義した文字列とかは再度操作しようとしたときに新しい『ラッパーオブジェクト』が生成されるので古い『ラッパーオブジェクト』に行った変更は全て破棄される、といったイメージになりますね。

そして、この『ラッパーオブジェクト』の仕組みを利用したちょっとした処理の拡張もよく行われるのでご紹介しておきます。

JavaScriptでの文字列は『String.prototype』の機能を共有した『ラッパーオブジェクト』でラップされてから処理が走る、つまり元となる『String.prototype』にメソッドを追加してしまえば全ての文字列でそのメソッドを使用することができるようになります。

以下は『数値(整数)っぽい文字列のみかどうかを真偽値で返す処理』を追加したパターンです。

String.prototype.isStringNum = function(){ // String.prototypeに定義することで全ての文字列で使用できるメソッドになります。new演算子とかを使用する必要はありません。
    return /^\d*$/.test(this); // 元となる文字列(this)が正規表現『^\d*$』にマッチする場合に真偽値『true』を返します。
};

"1".isStringNum(); // 真偽値『true』が返って来ます。
"0121".isStringNum(); // 真偽値『true』が返って来ます。
"777".isStringNum(); // 真偽値『true』が返って来ます。
"3.14".isStringNum(); // 真偽値『false』が返って来ます。
"onePunchMan".isStringNum(); // 真偽値『false』が返って来ます。

『正規表現』の解説はこちらへどぞ、『this』の解説についてはこちらへどぞ。

JavaScriptでは元々用意されている根本的な関数(メソッド)も書き換えることが可能なので、それを利用したテクニックになりますね。

文字列操作が多いウェブサイトなんかのバリデーション処理(入力された文字列等が規定に則っているか判定する処理)に統一性を持たせることが出来ますので良かったら使ってみてください。

というわけで以上になります。JavaScriptの裏側の仕組みの解説でございました。

続いての記事では『windowオブジェクト』とか『グローバルオブジェクト』について色々とやっていきましょう。ではではこの辺で。またお会いしましょう。

この記事は桜舞が執筆致しました。

著者が愛する小型哺乳類

桜舞 春人 Sakurama Haruto

ISDN時代から様々なコンテンツを制作しているちょっと髪の毛が心配な東京在住のプログラマー。生粋のロングスリーパーで、10時間以上睡眠を取らないと基本的に体調が悪い。好きなだけ寝れる生活を送るのが夢。ゲームとスポーツと音楽が大好き。誰か髪の毛を分けて下さい。

記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。