さて、あなたの変数が一枚の紙であると想像してください-付箋紙。
注1:変数は付箋です。
さて、付箋は非常に小さいです。あなたはそれにほんの少しの情報を書くことができるだけです。より多くの情報を書きたい場合は、より多くの付箋が必要ですが、それは問題ではありません。付箋紙が無限にあると想像してみてください。
注2:少量の情報を保存する付箋が無限にあります。
いいですね、付箋に何を書けますか?私は書くことができます:
- はいまたはいいえ(ブール値)。
- 私の年齢(数字)。
- 私の名前(文字列)。
- 何もありません(未定義)。
- 落書きなど、私にはまったく意味がありません(null)。
ですから、付箋紙に簡単なもの(見下して原始的なものと呼びましょう)を書くことができます。
注3:付箋紙に原始的なものを書くことができます。
だから30
、今夜私の場所で投げる小さなパーティーのためにチーズを30枚買うように思い出させるために、付箋紙に書いたとしましょう(友達はほとんどいません)。
付箋紙を冷蔵庫に置いてみると、妻が別の付箋紙を冷蔵庫に置いているのがわかります30
(彼女の誕生日は今月の30日です)。
Q:両方の付箋は同じ情報を伝えますか?
A:はい、どちらもそう言っています30
。30枚のチーズなのか、その月の30日なのかはわかりません。率直に言って、私たちは気にしません。よく知らなかった人にとっては、それはすべて同じです。
var slicesOfCheese = 30;
var wifesBirthdate = 30;
alert(slicesOfCheese === wifesBirthdate); // true
注4:同じものが書かれている2つの付箋は、2つの異なる付箋であっても、同じ情報を伝えます。
私は今夜本当に興奮しています-古い友達と一緒に遊んで、素晴らしい時間を過ごしています。それから私の友人の何人かは私に電話をして、彼らがパーティーに参加することができないだろうと言います。
だから私は冷蔵庫に行き30
、付箋紙(妻の付箋紙ではなく、彼女を非常に怒らせる)を消して、それをにし20
ます。
注5:付箋に書かれているものを消して他の何かを書くことができます。
Q:それはすべて問題ありませんが、妻がチーズを買いに出かけているときに手に入れることができる食料品のリストを書きたいと思ったらどうしますか。彼女はすべてのアイテムに付箋を書く必要がありますか?
A:いいえ、彼女は長い紙のリストを取り、その紙に食料品のリストを書きます。それから彼女は食料品のリストをどこで見つけるかを私に告げる付箋を書きました。
では、ここで何が起こっているのでしょうか。
- 食料品のリストは明らかに単純な(erm ...プリミティブ)データではありません。
- 私の妻はそれを長い紙に書いた。
- 彼女は付箋紙にそれを見つける場所を書きました。
ハニー、食料品のリストはキーボードの下にあります。
要点をまとめると:
- 実際のオブジェクト(食料品のリスト)は私のキーボードの下にあります。
- 付箋はそれを見つける場所(オブジェクトのアドレス)を教えてくれます。
注6:参照値は、オブジェクト(オブジェクトが見つかるアドレス)への参照です。
Q: 2つの付箋が同じことを言っていることをどうやって知ることができますか?私が最初の買い物リストを置き忘れた場合に備えて、妻が別の買い物リストを作成し、それについて別の付箋を書いたとしましょう。両方のリストは同じことを言っていますが、付箋は同じことを言っていますか?
A:いいえ。最初の付箋は最初のリストの場所を示しています。2つ目は、2つ目のリストの場所を示しています。2つのリストが同じことを言っているかどうかは関係ありません。それらは2つの異なるリストです。
var groceryList1 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"];
var groceryList2 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"];
alert(groceryList1 === groceryList2); // false
注7: 2つの付箋は、同じオブジェクトを参照している場合にのみ同じ情報を伝えます。
これは、妻が食料品リストの場所を思い出させる2つの付箋を作成した場合、2つの付箋には同じ情報が含まれていることを意味します。したがって、この:
ハニー、食料品のリストはキーボードの下にあります。
次と同じ情報が含まれています。
食料品のリストがキーボードの下にあることを忘れないでください。
プログラミング用語で:
var groceryList1 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"];
var groceryList2 = groceryList1;
alert(groceryList1 === groceryList2); // true
JavaScriptのプリミティブと参照について知っておく必要があるのはこれだけです。ヒープや動的メモリ割り当てなどに取り組む必要はありません。これは、C /C++でプログラミングしている場合に重要です。
編集1:ああ、そして重要なことは、変数を渡すとき、基本的にプリミティブ値を値で渡し、参照値を参照で渡すことです。
これは、ある付箋に書かれたすべてのものを別の付箋にコピーしていることを示すための手の込んだ方法です(プリミティブ値をコピーするか参照をコピーするかは関係ありません)。
参照をコピーするとき、参照されているオブジェクトは移動しません(たとえば、妻の食料品リストは常にキーボードの下に残りますが、コピーした付箋を好きな場所に置くことができます-元の付箋は冷蔵庫に残ります)。
編集2: @LacVietによって投稿されたコメントに応えて:
手始めに、JavaScriptについて話しているのですが、JavaScriptにはスタックやヒープがありません。これは動的言語であり、JavaScriptのすべての変数は動的です。違いを説明するために、Cと比較します。
次のCプログラムについて考えてみます。
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
int c = a + b;
printf("%d", c);
return 0;
}
このプログラムをコンパイルすると、実行可能ファイルが得られます。実行可能ファイルは複数のセグメント(またはセクション)に分割されています。これらのセグメントには、スタックセグメント、コードセグメント、データセグメント、追加セグメントなどが含まれます。
- スタックセグメントは、関数または割り込みハンドラーが呼び出されたときにプログラムの状態を格納するために使用されます。たとえば、関数が関数を
f
呼び出すと、関数g
の状態f
(その時点でのレジスタ内のすべての値)がスタックに保存されます。g
制御をに戻すと、f
これらの値が復元されます。
- コードセグメントは、プロセッサによって実行される実際のコードを保持します。これには、プロセッサが実行する必要のある一連の命令が含まれています
add eax, ebx
(ここadd
で、はオペコード、eax
&ebx
は引数です)。この命令は、レジスタの内容を追加し、その結果をレジスタeax
にebx
格納しますeax
。
- データセグメントは、変数用のスペースを予約するために使用されます。たとえば、上記のプログラムでは、整数
a
、b
およびのスペースを予約する必要がありc
ます。さらに、文字列定数のスペースも予約する必要があります"%d"
。したがって、予約された変数は、メモリ内に固定アドレスを持ちます(リンクおよびロード後)。
- これらすべてに加えて、オペレーティングシステムによって少し余分なスペースも与えられます。これはヒープと呼ばれます。必要な追加のメモリは、このスペースから割り当てられます。このように割り当てられたメモリは、動的メモリと呼ばれます。
動的メモリを備えたプログラムを見てみましょう。
#include <stdio.h>
#include <malloc.h>
int main() {
int * a = malloc(3 * sizeof(int));
a[0] = 3;
a[1] = 5;
a[2] = 7;
printf("a: %d\nb: %d\nc: %d\n", a[0], a[1], a[2]);
return 0;
}
メモリを動的に割り当てたいので、ポインタを使用する必要があります。これは、同じ変数を使用して任意のメモリ位置を指すようにするためです(必ずしも毎回同じメモリ位置である必要はありません)。
そこで、。というint
ポインタ(int *
)を作成しますa
。のスペースa
はデータセグメントから割り当てられます(つまり、動的ではありません)。malloc
次に、ヒープから3つの整数に連続するスペースを割り当てるために呼び出します。最初のメモリアドレスint
が返され、ポインタに格納されますa
。
Q:私たちは何を学びましたか?
A:すべての変数に一定量のスペースが割り当てられます。各変数には固定アドレスがあります。ヒープから追加のメモリを割り当て、この追加のメモリのアドレスをポインタに格納することもできます。これは動的メモリスキームと呼ばれます。
概念的には、これは変数が付箋であると説明したものと似ています。すべての変数(ポインターを含む)は付箋です。ただし、ポインタはメモリ位置を参照するため(JavaScriptでオブジェクトを参照するようなものです)、特別です。
ただし、ここで類似点が終わります。違いは次のとおりです。
- Cでは、すべてが値によって渡されます(ポインター内のアドレスを含む)。参照を渡すには、ポインターを介した間接参照を使用する必要があります。JavaScriptはプリミティブを値で渡すだけです。参照の受け渡しはエンジンによって透過的に処理され、他の変数を渡すのと同じです。
- Cでは、のようなプリミティブデータ型へのポインタを作成できます
int
。JavaScriptでは、のようなプリミティブ値への参照を作成することはできませんnumber
。すべてのプリミティブは常に値で保存されます。
- Cでは、ポインタに対してさまざまな操作を実行できます。これはポインタ演算と呼ばれます。JavaScriptにはポインタがありません。参照のみがあります。したがって、ポインタ演算を実行することはできません。
これら3つに加えて、CとJavaScriptの最大の違いは、JavaScriptのすべての変数が実際にはポインターであるということです。number
JavaScriptは動的言語であるため、同じ変数を使用して、とstring
を異なる時点で格納できます。
JavaScriptはインタプリタ言語であり、インタプリタは通常C++で記述されています。したがって、JavaScriptのすべての変数は、ホスト言語のオブジェクト(プリミティブも含む)にマップされます。
JavaScriptで変数を宣言すると、インタープリターはその変数の新しいジェネリック変数を作成します。次に、値(プリミティブまたは参照)を割り当てると、インタープリターは単に新しいオブジェクトを割り当てます。内部的には、どのオブジェクトがプリミティブで、どれが実際にオブジェクトであるかを認識しています。
概念的には、次のようなことをするようなものです。
JSGenericObject ten = new JSNumber(10); // var ten = 10;
Q:これはどういう意味ですか?
A: JavaScriptのすべての値(プリミティブとオブジェクト)がヒープから割り当てられることを意味します。変数自体もヒープから割り当てられます。プリミティブがスタックから割り当てられ、オブジェクトのみがヒープから割り当てられると述べるのは誤りです。これがCとJavaScriptの最大の違いです。