ラッパーオブジェクト

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

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

// Stringの`toUpperCase`メソッドを呼び出せる
"string".toUpperCase(); // => "STRING"

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

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

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

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

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

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

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

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

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

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

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

// プリミティブの文字列は"string"型
const str = "文字列";
console.log(typeof str); // => "string"
// ラッパーオブジェクトは"object"型
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();

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

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

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

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

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

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

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

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

まとめ

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

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

1. このようなプリミティブ型からオブジェクト型への変換はボックス化(ボクシング)、逆にオブジェクト型からプリミティブ型への変換はボックス化解除(アンボクシング)と呼ばれます。