JavaScript はプリミティブ(文字列、数値など)をオブジェクトのように扱うことができます。また、それらはメソッドも提供しています。この後すぐに学んでいきますが、最初に、どのように動作するのか確認しましょう。なぜなら、プリミティブはオブジェクトではないからです(このチャプターでそれをさらに明確にしていきます)。
プリミティブとオブジェクトの主な違いを見ていきましょう。
プリミティブ
- プリミティブ型の値です。
- プリミティブ型は 7 つあります:
string
,number
,bigint
,boolean
,symbol
,null
,undefined
オブジェクト
- プロパティとして複数の値を保持することができます。
{}
で作ることができます。例えば:{name: "John", age: 30}
。JavaScriptでは他の種類のオブジェクトもあります。関数もオブジェクトです。
オブジェクトの最も良いところの1つは、そのプロパティの1つとして関数を保持することができることです:
let john = {
name: "John",
sayHi: function() {
alert("Hi buddy!");
}
};
john.sayHi(); // Hi buddy!
上の例では、sayHi
メソッドをもつ john
オブジェクトを作りました。
日付、エラー、HTML要素などで動作するような、多くの組み込みのオブジェクトが既に存在し、それらは異なるプロパティとメソッドを持っています。
しかし、これらの機能にはコストがかかります!
オブジェクトはプリミティブよりも “重い” です。内部の仕組みをサポートするために追加のリソースを必要とします。しかし、プロパティやメソッドはプログラミングをする上で非常に有用であり、JavaScriptエンジンはそれらの負担を減らすために最適化を試みます。
オブジェクトとしてのプリミティブ
JavaScriptの作成者が直面するパラドックスは次の通りです。:
- 文字列や数字のようなプリミティブに対してやりたいことがたくさんあります。 それらをメソッドとして実現することは素晴らしいことでしょう。
- プリミティブはできるだけ高速、かつ軽量でなければなりません。
解決策は少々野暮ですが次の通りです:
- プリミティブは依然としてプリミティブです。要望どおり、単一の値です。
- 言語は、文字列、数値、真偽値そしてシンボルのメソッドやプロパティにアクセスすることができます。
- 必要に応じて、追加の機能を提供する特別な “オブジェクトラッパー” が作られ、その後、破棄されます。
“オブジェクトラッパー” はプリミティブ型毎に異なり、String
, Number
, Boolean
, Symbol
と呼ばれます。従って、それらは異なるメソッドのセットを提供します。
例えば、大文字化された文字列を返す str.toUpperCase() というメソッドがあります。
次のように動作します:
let str = "Hello";
alert( str.toUpperCase() ); // HELLO
シンプルですよね? str.toUpperCase()
で実際に何が起こっているのでしょう:
- 文字列
str
はプリミティブです。なので、プロパティへアクセスした瞬間に、文字列の値を知る特別なオブジェクトが作られ、それはtoUpperCase()
のような便利なメソッドを持っています。 - メソッドは実行され、新たな文字列を返します(
alert
で表示されたものです) - 特別なオブジェクトは破棄され、プリミティブの
str
だけが残ります。
従って、プリミティブは依然として軽量なままですが、メソッドを提供できます。
JavaScriptエンジンはこの処理を高度に最適化しています。余分なオブジェクトの作成を完全にスキップするかもしれません。しかし、それは依然として仕様に準拠し、あたかもオブジェクトを作成したかのように動作しなければなりません。
数値は自身のメソッドを持っています。例えば、指定された精度に数値を丸める toFixed(n) です:
let n = 1.23456;
alert( n.toFixed(2) ); // 1.23
具体的なメソッドはチャプター 数値 と 文字列 で見ましょう。
String/Number/Boolean
は内部でのみ利用しますJavaなどの言語は new Number(1)
または new Boolean(false)
のような構文を使うことで明示的にプリミティブのための “ラッパーオブジェクト” を作ることが出来ます。
JavaScriptにおいても、歴史的な理由から可能ですが、強く 推奨しません。いくつかの場所で物事がおかしなことになっていくでしょう。
例:
alert( typeof 1 ); // "number"
alert( typeof new Number(1) ); // "object"!
オブジェクトは if
では常に true なので、アラートが表示されます。
let zero = new Number(0);
if (zero) { // zero は true, オブジェクトだからです
alert( "zero is truthy?!?" );
}
一方、new
をつけずに同じ関数 String/Number/Boolean
を使うことは、普通であり役に立ちます。それらは値を対応する型へ変換します: 文字列、数値、もしくは真偽値(プリミティブ)
例えば、これはまったく問題ありません:
let num = Number("123"); // string から number へ変換
特別なプリミティブ null
と undefined
は例外です。それらは対応する “ラッパーオブジェクト” を持たずメソッドを提供しません。ある意味では “最もプリミティブ” です。
このようなアクセスはエラーになります:
alert(null.test); // error
サマリ
- プリミティブは、
null
とundefined
の例外を除いて、多くの役立つメソッドを提供します。次のチャプターで詳細を学んでいきます。 - 形式的には、それらのメソッドは一時的なオブジェクトを通して行われます。しかしJavaScriptエンジンは内部的に最適化するようチューニングされているため、呼び出しのコストはかかりません。