ラッパーオブジェクト

JavaScriptのデータ型はプリミティブ型とオブジェクトに分けられます。(詳細は「データ型とリテラル」を参照)

次のコードでは文字列リテラルでプリミティブ型の値である文字列を定義しています。 プリミティブ型の値である文字列はStringオブジェクトのインスタンスではありません。 しかし、プリミティブ型の文字列においても、StringオブジェクトのインスタンスメソッドであるtoUpperCaseメソッドを呼び出せます。

// String#toUpperCaseを呼び出している
"string".toUpperCase(); // => "STRING"

プリミティブ型である文字列がStringのインスタンスメソッドを呼び出せることは一見不思議です。

この章では、プリミティブ型の値がなぜオブジェクトのメソッドを呼び出せるのかについて解説します。

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

プリミティブ型のデータのうち、真偽値(Boolean)、数値(Number) 、文字列(String)、シンボル(Symbol)にはそれぞれ対応するオブジェクトが存在します。たとえば、文字列に対応するオブジェクトとして、Stringオブジェクトがあります。

このStringオブジェクトをnewすることでStringオブジェクトのインスタンスを作ることができます。

// "string"の値をラップしたStringのインスタンスを生成
const string = new String("string");
// StringのインスタンスメソッドであるtoUpperCaseを呼び出す
string.toUpperCase(); // => "STRING"

このようにインスタンス化されたものは、プリミティブ型の値を包んだ(ラップした)オブジェクトといえます。 そのため、このようなオブジェクトをプリミティブ型の値に対してのラッパーオブジェクトと呼びます。

ラッパーオブジェクトとプリミティブ型の対応は次のとおりです。

ラッパーオブジェクト プリミティブ型
Boolean 真偽値 truefalse
Number 数値 12
String 文字列 "文字列"
Symbol シンボル Symbol("説明")

注記: undefinednullに対応するラッパーオブジェクトはありません。

ひとつ注意点として、ラッパーオブジェクトは名前のとおりオブジェクトです。 そのため、次のようにtypeof演算子でラッパーオブジェクトを見ると"object"です。

const string = "文字列";
console.log(typeof string); // => "string"
const stringWrapper = new String("文字列");
console.log(typeof stringWrapper); // => "object"

プリミティブ型の値からラッパーオブジェクトへの自動変換

JavaScriptでは、プリミティブ型の値に対してプロパティアクセスする時、自動で対応するラッパーオブジェクトに変換されます。 たとえば"string"という文字列は、自動的にnew String("string")のようなラッパーオブジェクトへ変換されています。 これにより、プリミティブ型の値である文字列がStringのインスタンスメソッドを呼び出すことができます。

const str = "string";
// プリミティブ型の値に対してメソッド呼び出しを行う
str.toUpperCase();
// `str`へアクセスする際に"string"がラッパーオブジェクトへ変換され、
// ラッパーオブジェクトはStringのインスタンスなのでメソッドを呼び出せる
// つまり、上のコードは下のコードと同じ意味である
(new String(str)).toUpperCase();

一方、明示的に作成したラッパーオブジェクトからプリミティブ型の値を取りだすこともできます。

ラッパーオブジェクト.valueOfメソッドを呼び出すことで、ラッパーオブジェクトから値を取り出せます。 たとえば、次のように文字列のラッパーオブジェクトからvalueOfメソッドで文字列を取りだせます。

const stringWrapper = new String("文字列");
// プリミティブ型の値を取得する
console.log(stringWrapper.valueOf()); // => "文字列"

このように、プリミティブ型の値はラッパーオブジェクトへの変換は自動的に行われます。1

JavaScriptでは、リテラルを使ったプリミティブ型の文字列とラッパーオブジェクトを使った文字列オブジェクトがあります。(真偽値や数値についても同様です) この2つを明示的に使い分ける利点はないため、常にリテラルを使うことを推奨します。 理由として次の3つが挙げられます。

  • 必要に応じて、プリミティブ型の文字列は自動的にラッパーオブジェクトに変換されるため
  • new String("string")のようにラッパーオブジェクトのインスタンスを扱う利点がないため
  • ラッパーオブジェクトをtypeof演算子で評価した結果が、プリミティブ型ではなく"object"となり混乱を生むため

これらの理由などから、プリミティブ型のデータはリテラルを使います。 常にリテラルを使うことでラッパーオブジェクトは意識する必要がなくなります。

// OK: リテラルを使う
const string = "文字列";
// NG: ラッパーオブジェクトを使う
const stringWrraper = new String("文字列");

まとめ

この章では、プリミティブ型の値がなぜメソッド呼び出しできるのかについて解説しました。 その仕組みの背景にはプリミティブ型に対応したラッパーオブジェクトの存在があります。 プリミティブ型の値のプロパティへアクセスする際に、自動的にラッパーオブジェクトへ変換されることでメソッド呼び出しなどが可能となっています。

「JavaScriptはすべてがオブジェクトである」と言われることがあります。 プリミティブ型はオブジェクトではありませんが、プリミティブ型に対応したラッパーオブジェクトが用意されています。(nullundefinedを除く) そのため、「すべてがオブジェクトのように見える」というのが正しい認識となるでしょう。

1. このような値型から参照型の変換は一般にボックス化(ボクシング)、逆の変換はボックス化解除(アンボクシング)と呼ばれます