44

「Web 開発者向けのプロフェッショナル Javascript」という本を読んだところ、「変数は参照値またはプリミティブ値によって割り当てられます。参照値はメモリに格納されたオブジェクトです」と書かれています。そして、プリミティブ値がどのように格納されるかについては何も述べていません。だから私はそれがメモリに保存されていないと思います。それに基づいて、次のようなスクリプトがある場合:

var foo = 123;

fooJavascriptは後で使用するために変数をどのように記憶しますか?

4

7 に答える 7

109

さて、あなたの変数が一枚の紙であると想像してください-付箋紙。

注1:変数付箋です

さて、付箋は非常に小さいです。あなたはそれにほんの少しの情報を書くことができるだけです。より多くの情報を書きたい場合は、より多くの付箋が必要ですが、それは問題ではありません。付箋紙が無限にあると想像してみてください。

注2:少量の情報を保存する付箋が無限にあります。

いいですね、付箋に何を書けますか?私は書くことができます:

  1. はいまたはいいえ(ブール値)。
  2. 私の年齢(数字)。
  3. 私の名前(文字列)。
  4. 何もありません(未定義)。
  5. 落書きなど、私にはまったく意味がありません(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:いいえ、彼女は長い紙のリストを取り、その紙に食料品のリストを書きます。それから彼女は食料品のリストをどこで見つけるかを私に告げる付箋を書きました。

では、ここで何が起こっているのでしょうか。

  1. 食料品のリストは明らかに単純な(erm ...プリミティブ)データではありません。
  2. 私の妻はそれを長い紙に書いた。
  3. 彼女は付箋紙にそれを見つける場所を書きました。

ハニー、食料品のリストはキーボードの下にあります。

要点をまとめると:

  1. 実際のオブジェクト(食料品のリスト)は私のキーボードの下にあります。
  2. 付箋はそれを見つける場所(オブジェクトのアドレス)を教えてくれます。

注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;
}

このプログラムをコンパイルすると、実行可能ファイルが得られます。実行可能ファイルは複数のセグメント(またはセクション)に分割されています。これらのセグメントには、スタックセグメント、コードセグメント、データセグメント、追加セグメントなどが含まれます。

  1. スタックセグメントは、関数または割り込みハンドラーが呼び出されたときにプログラムの状態を格納するために使用されます。たとえば、関数が関数をf呼び出すと、関数gの状態f(その時点でのレジスタ内のすべての値)がスタックに保存されます。g制御をに戻すと、fこれらの値が復元されます。
  2. コードセグメントは、プロセッサによって実行される実際のコードを保持します。これには、プロセッサが実行する必要のある一連の命令が含まれていますadd eax, ebx(ここaddで、はオペコード、eaxebxは引数です)。この命令は、レジスタの内容を追加し、その結果をレジスタeaxebx格納しますeax
  3. データセグメントは、変数用のスペースを予約するために使用されます。たとえば、上記のプログラムでは、整数abおよびのスペースを予約する必要がありcます。さらに、文字列定数のスペースも予約する必要があります"%d"。したがって、予約された変数は、メモリ内に固定アドレスを持ちます(リンクおよびロード後)。
  4. これらすべてに加えて、オペレーティングシステムによって少し余分なスペースも与えられます。これはヒープと呼ばれます。必要な追加のメモリは、このスペースから割り当てられます。このように割り当てられたメモリは、動的メモリと呼ばれます。

動的メモリを備えたプログラムを見てみましょう。

#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でオブジェクトを参照するようなものです)、特別です。

ただし、ここで類似点が終わります。違いは次のとおりです。

  1. Cでは、すべてが値によって渡されます(ポインター内のアドレスを含む)。参照を渡すには、ポインターを介した間接参照を使用する必要があります。JavaScriptはプリミティブを値で渡すだけです。参照の受け渡しはエンジンによって透過的に処理され、他の変数を渡すのと同じです。
  2. Cでは、のようなプリミティブデータ型へのポインタを作成できますint。JavaScriptでは、のようなプリミティブ値への参照を作成することはできませんnumber。すべてのプリミティブは常に値で保存されます。
  3. Cでは、ポインタに対してさまざまな操作を実行できます。これはポインタ演算と呼ばれます。JavaScriptにはポインタがありません。参照のみがあります。したがって、ポインタ演算を実行することはできません。

これら3つに加えて、CとJavaScriptの最大の違いは、JavaScriptのすべての変数が実際にはポインターであるということです。numberJavaScriptは動的言語であるため、同じ変数を使用して、とstringを異なる時点で格納できます。

JavaScriptはインタプリタ言語であり、インタプリタは通常C++で記述されています。したがって、JavaScriptのすべての変数は、ホスト言語のオブジェクト(プリミティブも含む)にマップされます。

JavaScriptで変数を宣言すると、インタープリターはその変数の新しいジェネリック変数を作成します。次に、値(プリミティブまたは参照)を割り当てると、インタープリターは単に新しいオブジェクトを割り当てます。内部的には、どのオブジェクトがプリミティブで、どれが実際にオブジェクトであるかを認識しています。

概念的には、次のようなことをするようなものです。

JSGenericObject ten = new JSNumber(10); // var ten = 10;

Q:これはどういう意味ですか?

A: JavaScriptのすべての値(プリミティブとオブジェクト)がヒープから割り当てられることを意味します。変数自体もヒープから割り当てられます。プリミティブがスタックから割り当てられ、オブジェクトのみがヒープから割り当てられると述べるのは誤りです。これがCとJavaScriptの最大の違いです。

于 2012-11-07T11:22:02.567 に答える
73

Aは、またはvariableの 2 つの値タイプのいずれかを保持できます。primitive valuesreference values

  • Primitive valuesスタックに格納されるデータです。
  • Primitive value変数がアクセスする場所に直接格納されます。
  • Reference valuesヒープに格納されるオブジェクトです
  • Reference value変数 location に格納されているのは、オブジェクトが格納されているメモリ内の場所へのポインタです。
  • プリミティブ タイプには、、、、、Undefinedまたはが含まNullれます。BooleanNumberString

基礎:

オブジェクトはプロパティの集合体です。プロパティはobjectまたはを参照できますprimitivePrimitives are values、プロパティはありません。

更新しました:

JavaScript には、 StringNumberBooleanNullUndefinedSymbol (ES6 の新機能) の 6 つのプリミティブ データ型があります。null と undefined を除いて、すべてのプリミティブ値には、プリミティブ値をラップする同等のオブジェクトがあります。たとえば、Stringオブジェクトは文字列プリミティブをラップします。すべてのプリミティブは不変です。

于 2012-11-07T09:30:02.143 に答える
4

JavaScript では、に格納されてPrimitive valuesいるデータstackです。

Primitive value変数がアクセスする場所に直接格納されます。

そしては に格納されてReference valuesいるオブジェクトheapです。

変数 location に格納される参照値は、オブジェクトが格納されているメモリ内の場所へのポインタです。

JavaScript は 5 つのプリミティブ データ型をサポートしています: number, string, Boolean, undefined, and null.

これらの型は、より複雑な型を構築できる基本的なビルディング ブロックであるため、プリミティブ型と呼ばれます。

5 つのうち、number, string, and Boolean実際にデータを格納するという意味での実際のデータ型のみです。

Undefined and null特殊な状況下で発生するタイプです。のprimitive typeメモリ内のサイズは固定です。たとえば、数値は 8 バイトのメモリを占有し、ブール値は 1 ビットだけで表すことができます。

また、参照型は任意の長さにすることができます。サイズは固定されていません。

于 2012-11-07T09:40:38.427 に答える