JavaScript przechodzi obiekty według odniesienia. To ma sens. Ale gdy zaczniesz manipulować tymi obiektami, wszystko działa w sposób, który wydaje się nieintuicyjny. Pozwól mi zaoferować przykład:

var a, b;

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

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

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

Jest to dobrze i dobre, ponieważ teraz b ma wskaźnik do a, więc oczekuje się, że przypisanie rzeczy do a wpłynie również b.

Ale jeśli to zrobię:

a = a['one'];

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

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

To jest dla mnie zaskakujące. Spodziewam się a i b, aby nadal być taki sam (i być {x2}}, ponieważ a['one'] został wcześniej ustawiony na {} i a został ustawiony na a['one']).

Ale tak nie jest. Wygląda na to, że a traci swój odniesienie do b, gdy jest przypisany do czegoś nowego, ale {x2}} utrzymuje wartość, która a została ustawiona na przed upływem a jego odniesienie do b.

Ale jeśli to zrobię:

a['two'] = 2;

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

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

Co? a wyraźnie stracił to odniesienie do b, ale b wydaje się nadal mieć pewne odniesienie do a.

Czy pusty obiekt {} wskazuje na pewne miejsce w pamięci, więc każda zmienna odniesienie do tego, co wskazuje na to samo miejsce?

Czy ktoś może mi to wyjaśnić?

66
Philip Walton 30 listopad 2011, 02:02

5 odpowiedzi

Najlepsza odpowiedź

Po swojej przykładowej linii według linii:

a = {}

a Teraz odnosi się do nowego obiektu.

b = a;

b Teraz odnosi się do tego samego obiektu, który a Referencje. Zauważ, że nie odniesienia a.

a['one'] = {};

Nowy obiekt ma teraz indeks 'one', który odnosi się do innego nowego obiektu.

Kiedy to zrobisz

a = a['one'];

Ustawiasz a, aby odnieść się do a['one'], co jest tym nowym obiektem utworzonym, gdy zrobiłeś a['one'] = {}. b Nadal odnosi się do obiektu utworzonego za pomocą a = {}.

Mieszasz problemu, gdy mówisz "a stracił odniesienie do b", ponieważ a nie odnosi się do b, ani vice versa. a i b Patrz Obiekty i można je odnieść do innych obiektów. Lubię to:

Z a = {}; b = a

a
 \
  \
   { }
  /
 /
b

Następnie z a['one'] = {} dostajesz

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

Następnie z a = a['one'] dostajesz

a - - - - 
          \
   { one: { } }
  /
 /
b
208
Seth Carnegie 1 grudzień 2011, 18:02

: P jesteście malejącym do dzianinowych szczegółów i cieszę się, że zapytałeś, jak będziesz mądrzejszy.

Nie patrz na to pod względem wskazówek, ponieważ myślę, że to tam się zdezorientujesz. Pomyśl o tym raczej w kategoriach sterty (lub po prostu "pamięć", jeśli chcesz) i tabelę symbolu.

Zacznijmy, biorąc pierwsze kilka linii swojego kodu:

var a, b;

a = {}
b = a;

To, co zrobiłeś tutaj, jest utworzony jeden obiekt na sterty i dwa symbole na tabeli symbolu. Wygląda na to coś takiego:


symbol tabela :

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

sterty :

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

.


Oto, gdzie rzeczy stają się interesujące: obiekty mają własne "tabele symboli" (zwykle to są po prostu stoliki mieszające, ale dzwoniąc do niego tabelę symbolu może uczynić go wyraźniejszym).

Teraz, po następnym oświadczeniu, masz 3 rzeczy do rozważenia: tabeli symbolu globalnego, <object val 1> stół symbolu i sterty.

Uruchom następującą linię:

a['one'] = {}

A teraz wszystko wygląda:


globalny symbol stołu :

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

{x0}} Stół symbolu

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

sterty :

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

.


Teraz prowadziłeś następujący kod:

a = a['one'];

Powinno to mieć nadzieję, że wydaje się być trywialną zmianą. Wynik to:


globalny symbol stołu :

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

{x0}} Stół symbolu

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

sterty :

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

.


W następstwie lokalizacji pamięci do sterty powinny mieć nadzieję, że wyjaśniono, dlaczego miałeś wyjście.

Teraz rzeczy stają się jeszcze bardziej interesujące, ponieważ teraz robisz:

a['two'] = 2;

OK, więc weźmy ten krok po kroku.

  • a wskazuje na lokalizację pamięci 0x400004 która zawiera <object val 2>
  • <object val 2> jest pustym obiektem, a więc jego tabelę symbolu rozpoczyna się pusty
  • Uruchamiając tę linię, dodamy zmienną tabelę symbolu "dwa" do <object val 2>.

Jeśli nie jesteś jeszcze zmęczony patrzeniem na te diagramy, będziesz. Rzeczy wyglądają teraz:


globalny symbol stołu :

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

{x0}} Stół symbolu

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

{x0}} Stół symbolu

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

sterty :

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

.


Jeśli pilnie poświęcisz czas, aby śledzić lokalizacje pamięci, zobaczysz, że przeglądarka wyświetliła prawidłowe wyjście.

47
riwalk 29 listopad 2011, 22:23

Pomyśl o obiekcie anonimowym, jak sama ma nazwę:

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.

Kluczem jest pamiętać, że zmienne przechowywają odniesienia do obiektów , nie odniesienia do innych zmiennych. A ten sam obiekt może być określany przez dowolną liczbę zmiennych.

9
maerics 29 listopad 2011, 22:13

Obiekty w JavaScript mogą istnieć same bez konieczności nazwy. Na przykład:

{}

Jest nową instancją obiektu słownika.

a = {};

Tworzy nowy obiekt słownika i sprawia, że a odnosi się do niego. Teraz

b = a;

Sprawia, że b odnosi się do tego samego obiektu bazowego. Możesz następnie zrobić a punkt gdzie indziej:

a = "hi";

I b nadal wskazuje na ten sam obiekt słownikowy, który wcześniej zrobił. Zachowanie b nie jest związane z tym, jak zmienisz to, co a.

6
Greg Hewgill 29 listopad 2011, 22:10

O ile wiem, że nadpisałem A , więc myślę, że silnik oszczędza go w innej przestrzeni pamięci, podczas gdy b wciąż wskazując na stare a " S Adres pamięci (który w jakiś sposób nie zostanie zniszczony).

0
R01010010 8 listopad 2014, 22:28