JavaScript に限ることではないですが、現代の主なプログラミング言語の変数には値型と参照型というものがあります。英語でいうとプリミティブタイプとリファレンスタイプと呼ばれるようです。言語によって用語が異なるかもしれず、リファレンスタイプのことを、オブジェクト型/クラス型、と呼ぶ場合もあるようです。
さて、これは、なんだろうか、ということなのですが、説明します。
値型はわかりやすい。
値型には、数値/文字列/Boolean、などがあります。
値型は簡単です。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const value1 = 'ABC'; console.log(value1); // ABC と表示される const value2 = 'ABC'; console.log(value2); // ABC と表示されるdd | Cancel Title A short description Inline Don't Highlight Language Line Range (e.g. 3-5 or 3) Marked Lines console.log(value1 === value2) // true と表示される value1 = 'DEF'; // これは const で変数宣言しているためにエラーになる |
変数 value1 や value2 に値を入れて、その内容を比較したりすることができます。
自然に使える変数です。
参照型は少し変な感じ
対して、参照型は少し変な感じです。
参照型には、オブジェクト/配列/関数、などがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//例:参照型動作 イコールの比較 const reference1 = { name: 'ABC' }; console.log(reference1); // { name: 'ABC' } と表示される const reference2 = { name: 'ABC' }; console.log(reference2); // { name: 'ABC' } と表示される console.log(reference1 === reference2) // false と表示される console.log(reference1.toString() === reference2.toString()) // true と表示される //例:参照型動作 constなのに値変更できる reference1.name = 'DEF'; console.log(reference1); // { name: 'DEF' } と表示される //例:参照型動作 代入した場合はイコールで一致を比較できる const reference3 = reference1; console.log(reference1 === reference3) // true と表示される |
「例:参照型動作 イコールの比較」の部分を見てもらうと、イコールが一致しないことになっています。なので、.toString() という命令を使って文字列に変換してから比較してみました。
「例:参照型動作 constなのに値変更できる」を見てもらうと、定数定義として使う const を使っても 値を入れ替えることができています。
なんでしょうかこれは。
参照型の仕組み
仕組みを簡単に説明します。
reference1 や reference2 には、 人間には見えない形でプログラム内のメモリの位置(アドレス)を示す値が入っていると、考えます。実際に入っているはずなのですが、JavaScriptの場合は、プログラマから見えないのでそのように考えるということになります。
つまり、次のコードが実行される場合に
1 |
const reference1 = { name: 'ABC' }; |
referenece1 には「xxx1」 というメモリの位置が入ってい(ると考え)ます、メモリの「xxx1」という場所には、新しいオブジェクトが作られて、そのオブジェクトの実体というか本体というか、そちらが配置されていますが、その実体というか本体を直接見る方法はありません。
次のコードが実行される場合にも同じことがおきます。
1 |
const reference2 = { name: 'ABC' }; |
reference2 には「xxx2」というメモリの位置が入っていることになり、そのメモリの位置には、本体があるのですが、そこは見れない状態です。
そうすると、reference1 と refecence2 をイコールで比較しても、「xxx1」と「xxx2」とでは、場所が異なるので、一致しませんよ。ということになります。
「例:参照型動作 代入した場合はイコールで一致を比較できる」のところをみると、reference3 に reference1 を代入しています。これは、「xxx1」というメモリの位置をそのまま代入することになるので、reference1=「xxx1」、reference3=「xxx1」になり、イコールで一致を確認できるわけです。
「{}」でオブジェクトを定義する記号、オブジェクトリテラルというのは、オブジェクトをメモリに作り出す、という機能をもっていることになります。
昔の言語では、参照型はポインタと呼ばれるものだった。
昔から使われていて、現代でもよく使われている言語として、C++ があります。この言語は数値は2バイト、文字は1バイトとか、そういう変数のメモリ容量を気にしながらプログラミングする必要のある言語なのです。
オブジェクトを作り出すときには、オブジェクト全体がどのくらいのメモリ容量になるかを計算(自分で!)して、メモリの位置(アドレス)を決めて、容量を確保して、アドレスを、ポインタ型の変数(参照型変数)に入れる、という何段階もの手順を経て、オブジェクトを作るようなプログラムを書く必要がありました。
変数が不要になったら、メモリを解放するというのもプログラマがプログラムを書かなければいけませんでした。JSなどでは自動的に行われます。
C++は細かいメモリの制御ができるものの、難しい言語だと言われています。カーナビとか炊飯器のコンピュータとか、非力なコンピュータの内部ではメモリを節約するためによく使われます。たぶん。(よく知らない)
C++の場合は、JavaScriptで先程説明してた「参照型変数=ポインタ」とその中に入っている値の「メモリの位置=アドレス」というものは、実際に数値として取得し、見ることができていました。
JavaScriptだけではなく、現在の言語のほとんどは、このメモリの位置は見ることができなくなっていますが、参照型という仕組みはよく使われます。
まとめ
・値型は自然にわかる変数です。
・参照型は、イコールとか、const とかで思っているのと違う振る舞いをするように一見みえる動作をします。
・しかし、参照型は、参照という名のメモリの位置を示す値が入っていると考えるとわかります。
オブジェクトという参照型が存在するというのは、コンピュータの初期のころの名残なのかもしれませんが、仕組みを知ってみるとメモリを節約するプログラムを書けたりするので使いこなせるようになると便利です。
オブジェクトや配列のような参照型はイコールで比較しても、一致を確認することができないよ。ということは、プログラムを作る上でかなり重要なことなので、これはしっかり覚えておきましょう。