objects - Truco de código JavaScript: ¿Cuál es el valor de foo.x
object.assign array (6)
Como entiendo expresión:
foo.x = foo = {n: 2};
exactamente igual que:
foo.x = {n: 2} ;
foo = {n: 2};
Y después de esto se hizo obvio que:
bar=={n: 1, x: {n:2}};
foo=={n:2};
foo.x==undefined
Encontré este problema en una colección de preguntas de la entrevista frontal de GitHub:
var foo = {n: 1}; var bar = foo; foo.x = foo = {n: 2};
Pregunta: ¿Cuál es el valor de foo.x?
La respuesta es
undefined
.
He investigado un poco y entiendo que este problema es (corrígeme si me equivoco):
-
var foo = {n: 1};
declara un objetofoo
que tiene una propiedadn
igual a 1. -
var bar = foo;
declara unabar
objetos que se refiere al mismo objeto quefoo
. -
foo.x = foo = {n: 2};
que creo es igual afoo.x = (foo = {n: 2});
-
Y luego obtuve
foo.x
es igual aundefined
. Sin embargo, el valor debar.x
es el objeto{n:2}
.
Si
bar
y
foo
refieren al mismo objeto, ¿por qué
bar.x
obtuvo un valor mientras que
foo.x
undefined
está
undefined
?
Lo que realmente está sucediendo en
foo.x = foo = {n: 2};
?
Creo que en Javascript no puede asignar un valor a una propiedad que no existe si el objeto no está vacío. por lo tanto, en este caso, el objeto foo tiene un par de propiedades y valores que es {n: 1} así que dado que no está vacío y no tiene la propiedad ax, no puede asignar, pero dado que asigna al objeto de barra un valor que es el objeto foo , tendrá el valor sea cual sea el foo
Es una cuestión de entender que las variables de objeto son meras referencias a los objetos en JavaScript y no los objetos en sí.
var foo = {n: 1}
-> foo se refiere al objeto real {n: 1}
var bar = foo
-> bar ahora también es una referencia al objeto real {n: 1}
La parte difícil es, por supuesto, la tercera línea:
foo.x = foo = {n: 2}
Esto es equivalente a:
(reference to {n: 1}).x = (foo = {n: 2})
-> después de evaluar completamente esta línea, foo se convierte en una referencia al nuevo objeto {n: 2};
sin embargo, dado que foo se refiere al objeto original
{n: 1}
antes de la evaluación de la línea, el objeto original
{n: 1}
convierte en
{n: 1, x: [reference to]{n: 2}}
después del se evalúa la línea y se podrá acceder al objeto modificado a través de la
bar
referencia.
Si no hubiera una barra de referencia, el objeto original sería destruido
Pensé agregar otra, lo que encontré, una forma útil de pensar sobre esto.
Esas últimas asignaciones de variables son equivalentes a escribir
bar.x = foo = {n:2};
, porque esas variables son solo referencias a lo mismo en la memoria.
En otras palabras,
foo
y
bar
son, al principio, ambos haciendo referencia al mismo objeto,
{n:1}
.
Cuando utiliza
foo.x =
, accede a
{n:1}
y le agrega la propiedad
x
.
¡Esto podría hacerse con
bar
o
foo
porque ambos apuntan al mismo objeto en la memoria!
No hace ninguna diferencia.
Luego, cuando complete esa línea,
foo.x = foo = {n:2}
, está creando otro objeto
nuevo
en la memoria mediante la sintaxis literal del objeto y configurando
foo
para que apunte a
ese
objeto,
{n:2}
, en su lugar de lo que ahora es
{n:1, x: {n: 2}
.
Sin embargo, esto no afecta a lo que señaló
foo
cuando le agregaste la propiedad
x
.
Esto es bastante confuso, pero creo que tiene sentido que pienses en el hecho de que las variables son solo punteros a lugares / objetos en la memoria, y que la sintaxis literal del objeto no está cambiando el objeto previamente existente (aunque se vean similares). Está creando uno nuevo.
El comienzo de la respuesta aceptada a esta pregunta también puede ser útil.
foo.x = foo = {n: 2};
Aquí foo se refiere al objeto {n: 1} antes de la asignación, es decir, antes de que se ejecute la declaración.
La declaración puede reescribirse como foo.x = (foo = {n: 2});
En términos de objeto, la declaración anterior puede reescribirse como {n: 1} .x = ({n: 1} = {n: 2});
Dado que la asignación ocurre de derecha a izquierda solamente. Entonces, aquí solo tenemos que verificar que foo se esté refiriendo a qué objeto antes de que comience la ejecución.
Al resolver el RHS: foo = {n: 2} ; Ahora foo se refiere a {n: 2} ;
Volviendo al problema que nos queda:
foo.x = foo;
Ahora, foo.x en LHS sigue siendo {n: 1} .x, mientras que foo en RHS es {n: 2} .
Entonces, después de que se ejecute esta declaración, {n: 1} se convertirá en {n: 1, x: {n: 2}} con la barra todavía refiriéndose a ella. Donde como foo ahora se referirá a {n: 2} .
Entonces, en la ejecución, foo.x da indefinido ya que solo hay 1 valor en foo que es {n: 2}.
Pero si intenta ejecutar bar.x, obtendrá {n: 2}. O si solo ejecutas la barra, el resultado será
Objeto {n: 1, x: Objeto}
foo.x = foo = {n: 2};
determina que
foo.x
refiere a una propiedad
x
del objeto
{n: 1}
, asigna
{n: 2}
a
foo
y asigna el nuevo valor de
foo
-
{n: 2}
- a la propiedad
x
de
{n: 1}
objeto.
Lo importante es que el
foo
que se refiere
foo.x
se determina antes de que
foo
cambie.
Consulte la sección 11.13.1 de la especificación ES5 :
Deje que lref sea el resultado de evaluar LeftHandSideExpression .
Deje que rref sea el resultado de evaluar AssignmentExpression .
El operador de asignación se asocia de derecha a izquierda, por lo que obtiene:
foo.x = (foo = {n: 2})
El lado izquierdo se evalúa antes que el lado derecho.