JavaScript for impatient programmers (ES2022 版)
この本をサポートしてください: 購入 または 寄付
(広告、ブロックしないでください。)

12 値



この章では、JavaScript がどのような種類の値を持っているかを見ていきます。

  補助ツール: ===

この章では、厳密等価演算子を時々使用します。a === b は、ab が等しい場合に true と評価されます。正確にどういう意味なのかは、§13.4.2「厳密等価性 (=== および !==)」で説明しています。

12.1 型とは?

この章では、型を値の集合とみなします。例えば、型 boolean は集合 { false, true } です。

12.2 JavaScript の型階層

Figure 6: A partial hierarchy of JavaScript’s types. Missing are the classes for errors, the classes associated with primitive types, and more. The diagram hints at the fact that not all objects are instances of Object.

6 は JavaScript の型階層を示しています。この図から何を学ぶことができるでしょうか?

12.3 言語仕様の型

ECMAScript の仕様では、合計 8 つの型のみが認識されています。これらの型の名前は次のとおりです (私は仕様の名前ではなく、TypeScript の名前を使用しています)。

12.4 プリミティブ値 vs. オブジェクト

仕様では、値の間で重要な区別をしています

(ここで JavaScript に影響を与えた)Java とは対照的に、プリミティブ値は二級市民ではありません。プリミティブ値とオブジェクトの違いはより微妙です。要するに

それ以外にも、プリミティブ値とオブジェクトはよく似ています。どちらもプロパティ (キーと値のエントリ) を持ち、同じ場所で使用できます。

次に、プリミティブ値とオブジェクトについて詳しく見ていきます。

12.4.1 プリミティブ値 (略してプリミティブ)

12.4.1.1 プリミティブは不変です

プリミティブのプロパティを変更、追加、削除することはできません。

const str = 'abc';
assert.equal(str.length, 3);
assert.throws(
  () => { str.length = 1 },
  /^TypeError: Cannot assign to read only property 'length'/
);
12.4.1.2 プリミティブは値渡しされます

プリミティブは値渡しされます: 変数 (パラメータを含む) には、プリミティブの内容が格納されます。プリミティブ値を変数に代入するか、関数の引数として渡すと、その内容がコピーされます。

const x = 123;
const y = x;
// `y` is the same as any other number 123
assert.equal(y, 123);

  値渡しと参照渡しとの違いを観察する

プリミティブ値は不変であり、値で比較されるため (次のサブセクションを参照)、値渡しとID渡し (JavaScript のオブジェクトに使用される) との違いを観察する方法はありません。

12.4.1.3 プリミティブは値で比較されます

プリミティブは値で比較されます: 2 つのプリミティブ値を比較するとき、それらの内容を比較します。

assert.equal(123 === 123, true);
assert.equal('abc' === 'abc', true);

この比較方法の何が特別なのかを知るには、読み進めて、オブジェクトがどのように比較されるかを調べてください。

12.4.2 オブジェクト

オブジェクトの詳細については、§28「オブジェクト」と次の章で説明しています。ここでは、主にプリミティブ値とどのように異なるかに焦点を当てています。

まず、オブジェクトを作成する 2 つの一般的な方法を調べてみましょう

12.4.2.1 オブジェクトはデフォルトで可変です

デフォルトでは、オブジェクトのプロパティを自由に変更、追加、削除できます。

const obj = {};

obj.count = 2; // add a property
assert.equal(obj.count, 2);

obj.count = 3; // change a property
assert.equal(obj.count, 3);
12.4.2.2 オブジェクトはID渡しされます

オブジェクトはID渡しされます (私の用語): 変数 (パラメータを含む) には、オブジェクトのIDが格納されます。

オブジェクトの ID は、ヒープ (JavaScript エンジンの共有メインメモリと考えてください) 上のオブジェクトの実際のデータへのポインタ (または透過的な参照) のようなものです。

オブジェクトを変数に代入するか、関数の引数として渡すと、その ID がコピーされます。各オブジェクトリテラルは、ヒープ上に新しいオブジェクトを作成し、その ID を返します。

const a = {}; // fresh empty object
// Pass the identity in `a` to `b`:
const b = a;

// Now `a` and `b` point to the same object
// (they “share” that object):
assert.equal(a === b, true);

// Changing `a` also changes `b`:
a.name = 'Tessa';
assert.equal(b.name, 'Tessa');

JavaScript は、ガベージコレクションを使用してメモリを自動的に管理します。

let obj = { prop: 'value' };
obj = {};

これで、obj の古い値 { prop: 'value' }ガベージ (もう使用されていない) になりました。JavaScript は、(十分な空きメモリがある場合は、決して行われない可能性もありますが) ある時点で自動的にガベージコレクションを行います (メモリから削除します)。

  詳細: ID渡し

「ID渡し」とは、オブジェクトのID (透過的な参照) が値渡しされることを意味します。このアプローチは、「共有渡し」とも呼ばれます。

12.4.2.3 オブジェクトはIDで比較されます

オブジェクトはIDで比較されます (私の用語): 2 つの変数が等しいのは、同じオブジェクトIDが含まれている場合のみです。内容が同じ異なるオブジェクトを参照している場合は、等しくありません。

const obj = {}; // fresh empty object
assert.equal(obj === obj, true); // same identity
assert.equal({} === {}, false); // different identities, same content

12.5 演算子 typeofinstanceof: 値の型とは?

2 つの演算子 typeofinstanceof を使用すると、指定された値 x の型を判断できます。

if (typeof x === 'string') ···
if (x instanceof Array) ···

どのように異なるのでしょうか?

  経験則: typeof はプリミティブ値用、instanceof はオブジェクト用

12.5.1 typeof

表 2: typeof 演算子の結果。
x typeof x
undefined 'undefined'
null 'object'
Boolean 'boolean'
Number 'number'
Bigint 'bigint'
String 'string'
Symbol 'symbol'
Function 'function'
その他すべてのオブジェクト 'object'

2 には、typeof のすべての結果がリストされています。これらは、言語仕様の 7 つの型にほぼ対応しています。残念ながら、2 つの違いがあり、それらは言語の癖です。

これらは typeof を使用した例のいくつかです

> typeof undefined
'undefined'
> typeof 123n
'bigint'
> typeof 'abc'
'string'
> typeof {}
'object'

  練習: typeof に関する 2 つの練習

12.5.2 instanceof

この演算子は、値 x がクラス C によって作成されたかどうかという質問に答えます。

x instanceof C

たとえば

> (function() {}) instanceof Function
true
> ({}) instanceof Object
true
> [] instanceof Array
true

プリミティブ値は、何もインスタンスではありません

> 123 instanceof Number
false
> '' instanceof String
false
> '' instanceof Object
false

  練習: instanceof

exercises/values/instanceof_exrc.mjs

12.6 クラスとコンストラクタ関数

JavaScript のオブジェクトの元のファクトリは、コンストラクタ関数です。これは、new 演算子を介して呼び出すと、自身の「インスタンス」を返す通常の関数です。

ES6 では、クラスが導入されました。これは、主にコンストラクタ関数の構文をより良くしたものです。

この本では、コンストラクタ関数クラスという用語を同じ意味で使用しています。

クラスは、仕様の単一の型 object をサブタイプに分割していると見なすことができます。これにより、仕様の制限された 7 つの型よりも多くの型が得られます。各クラスは、それによって作成されたオブジェクトの型です。

12.6.1 プリミティブ型に関連付けられたコンストラクタ関数

各プリミティブ型 (undefined および null の仕様内部の型を除く) には、関連付けられたコンストラクタ関数 (クラスと考えてください) があります。

これらの各関数は、いくつかの役割を果たします。例えば、Number

12.6.1.1 プリミティブ値をラップする

プリミティブ型に関連するコンストラクタ関数は、プリミティブ値をオブジェクトに変換する正規の方法を提供するので、ラッパー型とも呼ばれます。その過程で、プリミティブ値はオブジェクトに「ラップ」されます。

const prim = true;
assert.equal(typeof prim, 'boolean');
assert.equal(prim instanceof Boolean, false);

const wrapped = Object(prim);
assert.equal(typeof wrapped, 'object');
assert.equal(wrapped instanceof Boolean, true);

assert.equal(wrapped.valueOf(), prim); // unwrap

ラッピングは実際にはほとんど問題になりませんが、プリミティブにプロパティを与えるために、言語仕様の内部で使用されています。

12.7 型間の変換

JavaScriptでは、値を他の型に変換する方法が2つあります。

12.7.1 型間の明示的な変換

プリミティブ型に関連付けられた関数は、値をその型に明示的に変換します。

> Boolean(0)
false
> Number('123')
123
> String(123)
'123'

値をオブジェクトに変換するためにObject()を使用することもできます。

> typeof Object(123)
'object'

次の表で、この変換がどのように機能するかを詳しく説明します。

x Object(x)
undefined {}
null {}
boolean new Boolean(x)
number new Number(x)
bigint BigIntのインスタンス (newTypeErrorをスローします)
string new String(x)
symbol Symbolのインスタンス (newTypeErrorをスローします)
object x

12.7.2 型強制(型間の自動変換)

多くの操作では、JavaScriptはオペランド/パラメータの型が一致しない場合、自動的に変換します。この種の自動変換は型強制と呼ばれます。

たとえば、乗算演算子はオペランドを数値に型強制します。

> '7' * '3'
21

多くの組み込み関数も型強制を行います。たとえば、Number.parseInt()は、解析する前にパラメータを文字列に型強制します。それは次の結果を説明します。

> Number.parseInt(123.45)
123

数値123.45は、解析される前に文字列'123.45'に変換されます。解析は、最初の数字以外の文字の前で停止するため、結果は123になります。

  練習問題: 値のプリミティブ型への変換

exercises/values/conversion_exrc.mjs

  クイズ

クイズアプリをご覧ください。