funciona - Puntero de Javascript/locura de referencia. ¿Alguien puede explicar esto?
javascript definicion (5)
: P Estás descendiendo a los detalles arenosos de punto y me alegro de que hayas preguntado, ya que serás más sabio al final.
No lo mires en términos de consejos, porque creo que es ahí donde te confundes. Piénselo más bien en términos del montón (o simplemente "memoria" si se quiere) y la tabla de símbolos.
Comencemos por tomar las primeras líneas de su código:
var a, b;
a = {}
b = a;
Lo que ha hecho aquí se crea un objeto en el montón y dos símbolos en la tabla de símbolos. Se ve algo como esto:
Tabla de símbolos :
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400000 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
Heap :
+----------+-----------------+
| Location | Value |
+----------+-----------------+
| 0x400000 | <object val 1> |
+----------+-----------------+
.
Aquí es donde las cosas se ponen interesantes: los objetos tienen sus propias "tablas de símbolos" (generalmente estas son solo tablas hash, pero llamarlo una tabla de símbolos puede hacerlo más claro).
Ahora, después de su próxima declaración, tiene 3 cosas que considerar: la tabla de símbolos global, la tabla de símbolos <object val 1>
y el montón.
Ejecute la siguiente línea:
a[''one''] = {}
Y ahora las cosas se ven así:
Tabla de símbolos globales :
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400000 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
<object val 1>
''s Symbol Table
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| one | 0x400004 |
+--------+-----------------+
Heap :
+----------+-----------------+
| Location | Value |
+----------+-----------------+
| 0x400000 | <object val 1> |
+----------+-----------------+
| 0x400004 | <object val 2> | <---we created a new object on the heap
+----------+-----------------+
.
Ahora ejecutó el siguiente código:
a = a[''one''];
Esto debería ser un cambio trivial. El resultado es:
Tabla de símbolos globales :
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400004 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
<object val 1>
''s Symbol Table
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| one | 0x400004 |
+--------+-----------------+
Heap :
+----------+-----------------+
| Location | Value |
+----------+-----------------+
| 0x400000 | <object val 1> |
+----------+-----------------+
| 0x400004 | <object val 2> |
+----------+-----------------+
.
Seguir las ubicaciones de la memoria para el montón debería dejar claro por qué obtuviste el resultado que obtuviste.
Ahora las cosas se ponen aún más interesantes, porque ahora lo estás haciendo:
a[''two''] = 2;
Bien, entonces vamos a seguir este paso por paso.
-
a
apunta a la ubicación de memoria0x400004
que contiene<object val 2>
-
<object val 2>
es un objeto vacío, por lo que su tabla de símbolos comienza vacía - Al ejecutar esta línea, agregamos la variable "dos" a la tabla de símbolos del
<object val 2>
.
Si aún no estás cansado de mirar estos diagramas, lo serás. Las cosas ahora se ven así:
Tabla de símbolos globales :
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| a | 0x400004 |
+--------+-----------------+
| b | 0x400000 |
+--------+-----------------+
<object val 1>
''s Symbol Table
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| one | 0x400004 |
+--------+-----------------+
<object val 2>
''s Symbol Table
+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
| two | 0x400008 |
+--------+-----------------+
Heap :
+----------+-----------------+
| 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 se toma el tiempo diligentemente para seguir las ubicaciones de la memoria, verá que su navegador muestra la salida correcta.
Javascript pasa objetos por referencia. Esto tiene perfecto sentido. Pero una vez que comienzas a manipular esos objetos, todo actúa de una manera que no parece intuitiva. Déjame ofrecerte un ejemplo:
var a, b;
a = {}
b = a;
a[''one''] = {};
console.log( JSON.stringify(a) );
// outputs: {"one":{}}
console.log( JSON.stringify(b) );
// outputs: {"one":{}}
Esto está muy bien porque ahora b
tiene un puntero a, a
lo que se espera que asignar cosas a a
también afecte a b
.
Pero luego si hago esto:
a = a[''one''];
console.log( JSON.stringify(a) );
// outputs: {}
console.log( JSON.stringify(b) );
// outputs: {"one":{}}
Esto es sorprendente para mí. Espero que a
y b
sigan siendo los mismos (y que sean {}
ya que a[''one'']
se estableció previamente en {}
y a
se estableció en a[''one'']
).
Pero ese no es el caso. Parece que a
pierde su referencia a b
cuando se asigna a algo nuevo, pero b
mantiene el valor que a
se estableció antes de perder su referencia a b
.
Pero luego si hago esto:
a[''two''] = 2;
console.log( JSON.stringify(a) );
// outputs: {"two":2}
console.log( JSON.stringify(b) );
// outputs: {"one":{"two":2}}
¿Qué? a
claramente ha perdido su referencia a b
, pero b
parece tener todavía alguna referencia a a
.
¿El objeto vacío {}
apunta a algún lugar en la memoria por lo que cada variable que hace referencia a él ahora apunta al mismo lugar?
¿Alguien con una comprensión firme de esto me lo explica?
Los objetos en Javascript pueden existir solos sin necesidad de un nombre. Por ejemplo:
{}
es una nueva instancia de un objeto de diccionario.
a = {};
crea un nuevo objeto de diccionario y hace a
referencia a él. Ahora
b = a;
hace que b
refiera al mismo objeto subyacente. A continuación, puede hacer a
punto en otro lugar:
a = "hi";
y b
aún apunta al mismo objeto de diccionario que tenía antes. El comportamiento de b
no está relacionado con la forma en que cambias lo que apunta a.
Piense en el objeto anónimo como si tuviera un nombre:
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 clave es recordar que las variables contienen referencias a objetos , no referencias a otras variables. Y el mismo objeto puede ser referido por cualquier cantidad de variables.
Por lo que sé, sobrescribió una, así que supongo que el motor la guarda en otro espacio de memoria, mientras que b todavía apunta a la dirección de memoria del viejo a (que de alguna manera no se destruye).
Siguiendo tu ejemplo línea por línea:
a = {}
a
ahora hace referencia al nuevo objeto.
b = a;
b
ahora hace referencia al mismo objeto que a
referencias. Tenga en cuenta que no hace referencia a
.
a[''one''] = {};
El nuevo objeto ahora tiene un índice ''one''
que hace referencia a otro objeto nuevo.
Cuando tu lo hagas
a = a[''one''];
Está configurando a
para referirse a a[''one'']
, que es ese nuevo objeto que creó cuando hizo a[''one''] = {}
. b
aún hace referencia al objeto que creaste con a = {}
.
Está confundiendo el problema cuando dice " a
ha perdido su referencia a b
" porque a
no se refiere a b
, ni viceversa. b
refieren a objetos , y pueden hacerse para referirse a otros objetos. Me gusta esto:
Con a = {}; b = a
a = {}; b = a
, obtienes
a
/
/
{ }
/
/
b
Entonces con a[''one''] = {}
obtienes
a
/
/
{ one: { } }
/
/
b
Entonces con a = a[''one'']
obtienes
a - - - -
/
{ one: { } }
/
/
b