64 votes

La folie des pointeurs/références en Javascript. Quelqu'un peut-il expliquer cela ?

Javascript transmet les objets par référence. C'est parfaitement logique. Mais une fois que vous commencez à manipuler ces objets, tout agit d'une manière qui semble peu intuitive. Laissez-moi 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 On s'attend donc à ce que l'assignation d'éléments à a affectera également b .

Mais si je fais ça :

a = a['one'];

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

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

C'est surprenant pour moi. Je m'attendais a y b d'être toujours le même (et d'être {} depuis a['one'] était précédemment réglé sur {} y a a été fixé à a['one'] ).

Mais ce n'est pas le cas. Il semble que a perd sa référence à b quand il est assigné à quelque chose de nouveau, mais b maintient la valeur que a a été fixé avant le a perdant 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}}

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

Est-ce que l'objet vide {} pointe vers un endroit de la mémoire, de sorte que toutes les variables qui y font référence pointent vers le même endroit ?

Quelqu'un qui connaît bien le sujet peut-il me l'expliquer ?

204voto

Seth Carnegie Points 45196

Je suis votre exemple ligne par ligne :

a = {}

a fait maintenant référence au nouvel objet.

b = a;

b fait maintenant référence au même objet que a références. 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 vous le faites

a = a['one'];

Vous êtes en train de régler a de se référer à a['one'] qui est le nouvel objet que vous avez créé quand vous avez fait a['one'] = {} . b fait toujours référence à l'objet que vous avez créé avec a = {} .

Vous confondez les choses quand vous dites " a a perdu sa référence à b " car a ne fait pas référence à b et vice versa. a y b se référer à objets et on peut les faire se référer à d'autres objets. Comme ceci :

Con a = {}; b = a vous obtenez

a
 \
  \
   { }
  /
 /
b

Ensuite, avec a['one'] = {} vous obtenez

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

Ensuite, avec a = a['one'] vous obtenez

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

46voto

Stargazer712 Points 8764

Vous entrez dans les détails de la vie quotidienne et je suis heureux que vous me le demandiez, car vous serez plus sage à la fin.

Ne le voyez pas en termes de pointeurs, car je pense que c'est là que vous vous trompez. Pensez-y plutôt en termes de tas (ou simplement de "mémoire" si vous voulez) et de table de 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 à quelque chose comme ça :


Tableau des symboles :

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

Amas :

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

.


C'est là que les choses deviennent intéressantes : Les objets ont leurs propres "tables de symboles" (en général, il s'agit simplement de tables de hachage, mais le fait d'appeler cela une table de symboles peut rendre les choses plus claires).

Maintenant, après votre prochaine déclaration, vous avez 3 choses à prendre en compte : La table globale des symboles, <object val 1> et le tas.

Exécutez la ligne suivante :

a['one'] = {}

Et maintenant les choses ressemblent à ça :


Tableau des symboles mondiaux :

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

<object val 1> Tableau des symboles de l'UE

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

Amas :

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

.


Maintenant, vous avez exécuté le code suivant :

a = a['one'];

Cette modification devrait, nous l'espérons, sembler triviale. Le résultat est :


Tableau des symboles mondiaux :

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

<object val 1> Tableau des symboles de l'UE

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

Amas :

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

.


En suivant les emplacements de la mémoire jusqu'au tas, vous devriez comprendre pourquoi vous avez obtenu le résultat que vous avez obtenu.

Maintenant les choses deviennent encore plus intéressantes, parce que maintenant vous faites :

a['two'] = 2;

Ok, alors prenons ça é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 par être vide.
  • En exécutant cette ligne, nous ajoutons la variable 'deux' à <object val 2> de la table des symboles.

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


Tableau des symboles mondiaux :

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

<object val 1> Tableau des symboles de l'UE

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

<object val 2> Tableau des symboles de l'UE

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

Amas :

+----------+-----------------+
| 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 le temps de suivre les emplacements de mémoire, vous verrez que votre navigateur a affiché la sortie correcte.

9voto

maerics Points 47743

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 et non des références à d'autres variables. Et un même objet peut être référencé par un nombre quelconque de variables.

6voto

Greg Hewgill Points 356191

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 a y faire référence. Maintenant

b = a;

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

a = "hi";

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

0voto

Rimbuaj Points 329

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X