web-dev-qa-db-fra.com

Astuce de code JavaScript: Quelle est la valeur de foo.x

J'ai trouvé ce problème dans une collection de questions d'entrevue frontale GitHub:

var foo = {n: 1};
var bar = foo;
foo.x = foo = {n: 2};

Question: Quelle est la valeur de foo.x?

La réponse est undefined.

J'ai fait des recherches et je comprends ce problème (corrigez-moi si je me trompe):

  • var foo = {n: 1}; Déclare un objet foo dont la propriété n est égale à 1.
  • var bar = foo; Déclare un objet bar qui fait référence au même objet que foo.
  • foo.x = foo = {n: 2}; Qui je crois est égal à foo.x = (foo = {n: 2});
  • Et puis j'ai foo.x Égal à undefined. Cependant, la valeur de bar.x Est l'objet {n:2}.

Si bar et foo font référence au même objet, pourquoi bar.x A-t-il obtenu une valeur alors que foo.x Est undefined? Que se passe-t-il vraiment dans foo.x = foo = {n: 2};?

52
AndyHu
foo.x = foo = {n: 2};

détermine que foo.x fait référence à une propriété x du {n: 1} objet, attribue {n: 2} à foo et affecte la nouvelle valeur de foo - {n: 2} - à la propriété x du {n: 1} objet.

L'important est que le foo que foo.x fait référence à est déterminé avant que foo change.

Voir section 11.13.1 de la spécification ES5 :

  1. Soit lref le résultat de l'évaluation LeftHandSideExpression .

  2. Soit rref le résultat de l'évaluation de l'expression d'affectation .

L'opérateur d'affectation associe de droite à gauche, vous obtenez donc:

foo.x = (foo = {n: 2})

Le côté gauche est évalué avant le côté droit.

51
Ry-

foo.x = foo = {n: 2};

Ici, foo fait référence à {n: 1} objet avant l'affectation, c'est-à-dire avant que l'instruction ne soit exécutée.

L'instruction peut être réécrite sous la forme foo.x = (foo = {n: 2});

En termes d'objet, l'instruction ci-dessus peut être réécrite sous la forme {n: 1} .x = ({n: 1} = {n: 2});

Étant donné que l'affectation se produit de droite à gauche uniquement. Il nous suffit donc de vérifier que foo fait référence à quel objet avant le début de l'exécution.

Sur la résolution du R.H.S: foo = {n: 2}; Maintenant foo fait référence à {n: 2};

Pour revenir sur le problème, il nous reste:

foo.x = foo;

Maintenant foo.x sur L.H.S est toujours {n: 1} .x alors que foo sur R.H.S est {n: 2}.

Donc, après l'exécution de cette instruction {n: 1} deviendra {n: 1, x: {n: 2}} avec une barre qui y fait référence. Alors que foo fera maintenant référence à {n: 2}.

Donc lors de l'exécution, foo.x donne undefined car il n'y a qu'une seule valeur dans foo qui est {n: 2}.

Mais si vous essayez d'exécuter bar.x, cela donnera {n: 2}. Ou si vous exécutez simplement la barre, le résultat sera

Objet {n: 1, x: Objet}

23
Shardul Singh Rana

J'ai pensé en ajouter un autre, ce que j'ai trouvé, une façon utile de penser à ce sujet.

Ces dernières affectations de variables équivalent à écrire bar.x = foo = {n:2};, car ces variables ne sont que des références à la même chose en mémoire.

En d'autres termes, foo et bar font tout d'abord référence au même objet, {n:1}. Lorsque vous utilisez foo.x =, vous accédez à {n:1} et en y ajoutant la propriété x. Cela pourrait être fait avec bar ou foo car ils pointent tous les deux vers le même objet en mémoire! Ça ne fait aucune différence.

Ensuite, lorsque vous avez terminé cette ligne, foo.x = foo = {n:2}, vous en créez un autre tout nouvel objet en mémoire via la syntaxe littérale de l'objet et définissez foo pour pointer vers cet objet , {n:2}, au lieu de ce qui est maintenant {n:1, x: {n: 2}. Cela n'affecte cependant pas ce que foo a souligné lorsque vous y avez ajouté la propriété x.

C'est assez déroutant, mais je pense qu'il est logique que vous pensiez au fait que les variables ne sont que des pointeurs vers des lieux/objets en mémoire, et que la syntaxe littérale de l'objet ne change pas l'objet existant précédemment (même si elles semblent similaires). Il en crée un tout nouveau.

Le début de la réponse acceptée à cette question peut également être utile.

4
Zach Nagatani

Il s'agit de comprendre que les variables d'objet sont simplement des références aux objets en JavaScript et non des objets eux-mêmes.

var foo = {n: 1} -> foo fait référence à l'objet réel {n: 1} var bar = foo -> bar est maintenant aussi une référence à l'objet réel {n: 1}

La partie délicate est bien sûr la 3ème ligne: foo.x = foo = {n: 2}

Cela équivaut à: (reference to {n: 1}).x = (foo = {n: 2}) -> une fois cette ligne complètement évaluée, foo devient une référence au nouvel objet {n: 2}; cependant, puisque foo fait référence à l'objet d'origine {n: 1} avant l'évaluation de la ligne, l'objet d'origine {n: 1} devient {n: 1, x: [reference to]{n: 2}} après évaluation de la ligne, et l'objet modifié sera accessible via la référence bar. S'il n'y avait pas de barre de référence, l'objet d'origine serait détruit

3
Hou

Si je comprends bien l'expression:

foo.x = foo = {n: 2};

exactement la même chose que:

foo.x = {n: 2} ; 
foo = {n: 2};

Et après cela, il est devenu évident que:

 bar=={n: 1, x: {n:2}};
 foo=={n:2};
 foo.x==undefined
3
Ron Lavit