web-dev-qa-db-fra.com

Pointeur Javascript / folie de référence. Quelqu'un peut-il expliquer cela?

Javascript passe des objets par référence. Cela est parfaitement logique. Mais une fois que vous commencez à manipuler ces objets, tout agit d'une manière qui ne vous semble pas intuitive. Permettez-moi de vous donner un exemple:

var a, b;

a = {}
b = a;
a['one'] = {};

console.log( JSON.stringify(a) );
// outputs: {"one":{}}

console.log( JSON.stringify(b) );
// outputs: {"one":{}}

C'est très bien car maintenant b a un pointeur sur a donc on s'attend à ce que l'attribution de choses à a affecte également b.

Mais si je fais ça:

a = a['one'];

console.log( JSON.stringify(a) );
// outputs: {}

console.log( JSON.stringify(b) );
// outputs: {"one":{}}

Cela m’étonne. Je m'attends à ce que a et b soient toujours les mêmes (et soient {} puisque a['one'] était précédemment défini sur {} et a a été défini sur a['one']).

Mais ce n'est pas le cas. Il semble que a perd sa référence à b lorsqu'il est affecté à quelque chose de nouveau, mais b conserve la valeur que a a été définie avant a perd sa référence à b.

Mais si je fais ça:

a['two'] = 2;

console.log( JSON.stringify(a) );
// outputs: {"two":2}

console.log( JSON.stringify(b) );
// outputs: {"one":{"two":2}}

Quelle? a a clairement perdu sa référence à b, mais b semble toujours avoir une référence à a.

L'objet vide {} pointer vers un endroit en mémoire donc chaque variable qui y fait référence pointe maintenant vers le même endroit?

Est-ce que quelqu'un avec une compréhension ferme de cela peut me l'expliquer?

61
Philip Walton

En suivant votre exemple ligne par ligne:

a = {}

a fait désormais référence au nouvel objet.

b = a;

b fait désormais référence au même objet que a fait référence. Notez qu'il ne fait pas référence à a.

a['one'] = {};

Le nouvel objet a maintenant un index 'one' qui fait référence à un autre nouvel objet.

Quand tu fais

a = a['one'];

Vous définissez a pour faire référence à a['one'], qui est le nouvel objet que vous avez créé lorsque vous avez fait a['one'] = {}. b fait toujours référence à l'objet que vous avez créé avec a = {}.

Vous confondez le problème lorsque vous dites que "a a perdu sa référence à b" car a ne fait pas référence à b, ni vice versa. a et b font référence à objets, et ils peuvent être faits pour faire référence à d'autres objets. Comme ça:

Avec a = {}; b = a, vous obtenez

a
 \
  \
   { }
  /
 /
b

Puis avec a['one'] = {} vous obtenez

a
 \
  \
   { one: { } }
  /
 /
b

Puis avec a = a['one'] vous obtenez

a - - - - 
          \
   { one: { } }
  /
 /
b
202
Seth Carnegie

: P Vous descendez dans les détails granuleux et je suis content que vous ayez demandé, car vous serez plus sage à la fin.

Ne regardez pas cela en termes de pointeurs, car je pense que c'est là que vous vous embrouillez. Pensez-y plutôt en termes de tas (ou simplement de "mémoire" si vous voulez) et de la table des symboles.

Commençons par prendre les premières lignes de votre code:

var a, b;

a = {}
b = a;

Ce que vous avez fait ici, c'est créer un objet sur le tas et deux symboles sur la table des symboles. Cela ressemble à ceci:


Table des symboles:

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|      a |        0x400000 |
+--------+-----------------+
|      b |        0x400000 |
+--------+-----------------+

tas:

+----------+-----------------+
| Location | Value           |
+----------+-----------------+
| 0x400000 | <object val 1>  |
+----------+-----------------+

.


Voici où les choses deviennent intéressantes: les objets ont leurs propres "tables de symboles" (généralement ce ne sont que des tables de hachage, mais l'appeler une table de symboles peut le rendre plus clair).

Maintenant, après votre prochaine déclaration, vous devez prendre en compte 3 éléments: la table des symboles globale, la table des symboles de <object val 1> Et le tas.

Exécutez la ligne suivante:

a['one'] = {}

Et maintenant, les choses ressemblent à ceci:


Table des symboles globaux:

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|      a |        0x400000 |
+--------+-----------------+
|      b |        0x400000 |
+--------+-----------------+

Table de symboles de <object val 1>

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|    one |        0x400004 |
+--------+-----------------+

tas:

+----------+-----------------+
| Location | Value           |
+----------+-----------------+
| 0x400000 | <object val 1>  |
+----------+-----------------+
| 0x400004 | <object val 2>  |     <---we created a new object on the heap
+----------+-----------------+

.


Vous avez maintenant exécuté le code suivant:

a = a['one'];

J'espère que cela devrait être un changement insignifiant. Le résultat est:


Table des symboles globaux:

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|      a |        0x400004 |
+--------+-----------------+
|      b |        0x400000 |
+--------+-----------------+

Table de symboles de <object val 1>

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|    one |        0x400004 |
+--------+-----------------+

tas:

+----------+-----------------+
| Location | Value           |
+----------+-----------------+
| 0x400000 | <object val 1>  |
+----------+-----------------+
| 0x400004 | <object val 2>  | 
+----------+-----------------+

.


Il est à espérer que le fait de suivre les emplacements de mémoire jusqu'au tas devrait expliquer clairement pourquoi vous avez obtenu la sortie que vous avez obtenue.

Maintenant, les choses deviennent encore PLUS intéressantes, car maintenant vous faites:

a['two'] = 2;

Ok, prenons donc cette étape par étape.

  • a pointe vers l'emplacement mémoire 0x400004 qui contient <object val 2>
  • <object val 2> Est un objet vide, donc sa table de symboles commence vide
  • En exécutant cette ligne, nous ajoutons la variable "deux" à la table des symboles de <object val 2>.

Si vous n'êtes pas encore fatigué de regarder ces diagrammes, vous le serez. Les choses ressemblent maintenant à ceci:


Table des symboles globaux:

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|      a |        0x400004 |
+--------+-----------------+
|      b |        0x400000 |
+--------+-----------------+

Table de symboles de <object val 1>

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|    one |        0x400004 |
+--------+-----------------+

Table de symboles de <object val 2>

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|    two |        0x400008 |
+--------+-----------------+

tas:

+----------+-----------------+
| Location | Value           |
+----------+-----------------+
| 0x400000 | <object val 1>  |
+----------+-----------------+
| 0x400004 | <object val 2>  | 
+----------+-----------------+
| 0x400008 | 2 (literal val) |    <-- yes, even integers are stored on the heap
+----------+-----------------+        in JavaScript.

.


Si vous prenez diligemment le temps de suivre les emplacements de la mémoire, vous verrez que votre navigateur affiche la sortie correcte.

45
riwalk

Considérez l'objet anonyme comme ayant lui-même un nom:

a = {}; // The variable "a" now points to (holds) an anonymous object.
b = a; // "b" points to the same anonymous object held by "a".
a = 123; // "a" now holds some other value.
b; // "b" still holds the anonymous object.

La clé est de se rappeler que les variables contiennent des références à objets, pas des références à d'autres variables. Et le même objet peut être désigné par n'importe quel nombre de variables.

8
maerics

Les objets en Javascript peuvent exister par eux-mêmes sans avoir besoin d'un nom. Par exemple:

{}

est une nouvelle instance d'un objet dictionnaire.

a = {};

crée un nouvel objet dictionnaire et fait référence à a. Maintenant

b = a;

fait que b fait référence au même objet sous-jacent. Vous pouvez alors faire pointer a ailleurs:

a = "hi";

et b pointe toujours vers le même objet dictionnaire qu'auparavant. Le comportement de b n'est pas lié à la façon dont vous modifiez ce que a pointe vers.

6
Greg Hewgill

Autant que je sache, vous avez écrasé a donc je suppose que le moteur l'enregistre dans un autre espace mémoire, tandis que b pointe toujours vers l'ancien a ' l'adresse de mémoire de s (qui d'une manière ou d'une autre n'est pas détruite).

0
R01010010