@rui-ki

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

今日の疑問

Q&A

JavaScript・TypeScriptのプリミティブ型はオブジェクトではないのに、なぜメソッドが使えるのか?

はじめに

JavaScriptやTypeScriptを学習している中で、以下のような疑問を持ちました。

プリミティブ型はオブジェクトではないのに、なぜメソッドを使用できるのだろう?

例えば、JavaScriptでは文字列に対して以下のようなメソッドを使用できます。

const text = "hello";
console.log(text.toUpperCase());

実行結果

HELLO

しかし、文字列はプリミティブ型であり、オブジェクトではありません。

const text = "hello";
console.log(typeof text);

実行結果

string

typeof の結果は object ではなく string です。

それでは、なぜオブジェクトではないプリミティブ型に対して toUpperCase() のようなメソッドを使用できるのでしょうか。

今回はこの仕組みについて調べてみました。

プリミティブ型とは

JavaScriptには、プリミティブ型と呼ばれる基本的なデータ型があります。

主なプリミティブ型は以下の通りです。

  • string(文字列)
  • number(数値)
  • boolean(真偽値)
  • undefined
  • null
  • symbol
  • bigint

例えば、

const text = "hello"; // string
const number = 100; // number
const flag = true; // boolean

これらはオブジェクトではなく、プリミティブ値として扱われます。

オブジェクトではないのにメソッドが使える理由

結論から言うと、

JavaScriptが必要なタイミングで、プリミティブ値を一時的に対応するオブジェクトとして扱ってくれるためです。

例えば、以下のコードを見てみます。

const text = "hello";
console.log(text.toUpperCase());

text は string 型のプリミティブ値です。

しかし、toUpperCase() を実行する際、JavaScriptは内部的に一時的な String オブジェクトを生成します。

イメージとしては以下のような流れです。

"hello"

一時的にStringオブジェクトとして扱う

toUpperCase()を実行

処理が終わると一時的なオブジェクトは不要になる

コードで表すと、以下のようなイメージになります。

new String("hello").toUpperCase();

ただし、実際に自分で new String() を書く必要はありません。

JavaScriptが内部的に自動で行ってくれます。

ラッパーオブジェクトとは

プリミティブ値をオブジェクトとして扱うためのオブジェクトを、ラッパーオブジェクトと呼びます。

代表的な対応関係は以下の通りです。

プリミティブ型 ラッパーオブジェクト
string String
number Number
boolean Boolean

例えば、

const text = "hello";

の "hello" はプリミティブ型です。

一方で、

const text = new String("hello");

は String オブジェクトです。

この2つは似ていますが、厳密には異なります。

実際に比較してみる

プリミティブ型の文字列と、String オブジェクトを比較してみます。

const primitive = "hello";
const object = new String("hello");
console.log(typeof primitive);
console.log(typeof object);

実行結果

string
object

このように、

"hello"

は string 型ですが、

new String("hello")

は object 型になります。

通常は new String() を使用する必要はなく、プリミティブ型の string を使用します。

const text = "hello";

メソッドを使用する際は、JavaScriptが必要に応じて一時的にラッパーオブジェクトとして扱ってくれるためです。

NumberやBooleanでも同じ

この仕組みは文字列だけではありません。

Numberの場合

const number = 123.456;
console.log(number.toFixed(2));

実行結果

123.46

number はプリミティブ型ですが、toFixed() というメソッドを使用できます。

これはJavaScriptが必要に応じて一時的に Number オブジェクトとして扱うためです。

Booleanの場合

const flag = true;
console.log(flag.toString());

実行結果

true

Booleanについても同様に、メソッドを使用できます。

プリミティブ型にプロパティを追加しても保持されない

ここで少し気になることがあります。

オブジェクトであれば、プロパティを追加できます。

const user = {};
user.name = "Taro";
console.log(user.name);

実行結果

Taro

しかし、プリミティブ型ではどうでしょうか。

let text = "hello";
text.test = "test";
console.log(text.test);

実行結果

undefined

これは、プリミティブ値にアクセスするたびに一時的なラッパーオブジェクトが作られ、処理後に破棄されるためです。

イメージとしては以下のようになります。

text.test = "test"

一時的なStringオブジェクトを作成

testプロパティを追加

処理終了後、一時的なオブジェクトが破棄される

次に、

text.test

へアクセスすると、先ほどとは別の一時的なオブジェクトとして扱われます。

そのため、先ほど追加した test プロパティは存在せず、undefined になります。

TypeScriptでも同じ?

結論として、TypeScriptでも基本的な仕組みは同じです。

例えば、

const text: string = "hello";
console.log(text.toUpperCase());

TypeScriptでも string はプリミティブ型ですが、toUpperCase() を使用できます。

理由は、TypeScriptは最終的にJavaScriptに変換されて実行されるためです。

TypeScriptのコード

const text: string = "hello";
console.log(text.toUpperCase());

JavaScriptとして実行されるコード

const text = "hello";
console.log(text.toUpperCase());

実際にブラウザやNode.jsで実行されるのはJavaScriptです。

そのため、プリミティブ型からメソッドを使用できる仕組みはJavaScriptと同じです。

TypeScriptでは小文字と大文字に注意

TypeScriptでは以下のような型があります。

string
number
boolean

これらはプリミティブ型です。

一方で、

String
Number
Boolean

も存在します。

こちらはJavaScriptのラッパーオブジェクトに対応する型です。

通常は以下のように、小文字のプリミティブ型を使用します。

let name: string;
let age: number;
let isLogin: boolean;

大文字の String、Number、Boolean は基本的には使用しないことが推奨されています。

まとめ

JavaScriptのプリミティブ型は、オブジェクトではありません。

例えば、

const text = "hello";
console.log(typeof text); // string

しかし、以下のようにメソッドを使用できます。

text.toUpperCase();

これはJavaScriptがメソッドやプロパティへアクセスする際に、一時的に対応するラッパーオブジェクトとして扱ってくれるためです。

主な対応関係は以下の通りです。

プリミティブ型 ラッパーオブジェクト
string String
number Number
boolean Boolean

つまり、

プリミティブ型そのものはオブジェクトではないが、メソッドを使用する際にJavaScriptが一時的にラッパーオブジェクトとして扱ってくれるため、メソッドを使用できる。

という仕組みになっています。

最初は「オブジェクトではないのに、なぜメソッドがあるの?」と疑問に思いましたが、ラッパーオブジェクトの仕組みを知ることで理解することができました。

JavaScriptには、このように一見すると不思議に思える仕様がまだ多くありそうなので、今後も疑問に思ったことを調べて記事にしていきたいと思います。

0 likes

1Answer

こちらは不特定多数に質問をするところなので、記事であれば、記事として投稿されるのがよいと思います。

0Like

Your answer might help someone💌