Javascript указатель / ссылка сумасшествие.Может кто-нибудь объяснить это? - PullRequest
61 голосов
/ 30 ноября 2011

Javascript передает объекты по ссылке.Это имеет смысл.Но как только вы начинаете манипулировать этими объектами, все действует неинтуитивно.Позвольте мне привести пример:

var a, b;

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

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

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

Это все хорошо, потому что теперь b имеет указатель на a, поэтому ожидается, что присвоение a также повлияет на b.

Но тогда, если я сделаю это:

a = a['one'];

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

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

Это удивительно для меня.Я бы ожидал, что a и b будут по-прежнему одинаковыми (и будут {}, поскольку a['one'] ранее было установлено на {}, а a было установлено на a['one']).

Но это не тот случай.Похоже, что a теряет свою ссылку на b, когда ему назначено что-то новое, но b сохраняет значение, которое было установлено на a до a, теряя свою ссылку на b.

Но тогда, если я сделаю это:

a['two'] = 2;

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

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

Что?a явно потерял свою ссылку на b, но b, кажется, все еще имеет некоторую ссылку на a.

Указывает ли пустой объект {} на какое-то место в памяти, так что каждая переменнаяссылка на него теперь указывает на то же место?

Может ли кто-то, кто твердо понимает это, объяснить мне это?

Ответы [ 5 ]

202 голосов
/ 30 ноября 2011

Следуя вашему примеру, строка за строкой:

a = {}

a теперь ссылается на новый объект.

b = a;

b теперь ссылается на тот же объект, на который ссылается a. Обратите внимание, что он не ссылается на a.

a['one'] = {};

Новый объект теперь имеет индекс 'one', который ссылается на другой новый объект.

Когда вы делаете

a = a['one'];

Вы устанавливаете a для ссылки на a['one'], который является тем новым объектом, который вы создали, когда сделали a['one'] = {}. b все еще ссылается на объект, который вы создали с помощью a = {}.

Вы путаете проблему, когда говорите "a потерял свою ссылку на b", потому что a не относится к b, и наоборот. a и b относятся к объектам , и они могут быть созданы для ссылки на другие объекты. Как это:

С a = {}; b = a вы получите

a
 \
  \
   { }
  /
 /
b

Тогда с a['one'] = {} вы получите

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

Тогда с a = a['one'] вы получите

a - - - - 
          \
   { one: { } }
  /
 /
b
45 голосов
/ 30 ноября 2011

: P Ты погружаешься в мельчайшие мелкие детали, и я рад, что ты спросил, так как ты будешь мудрее к концу.

Не смотрите на это с точки зрения указателей, потому что я думаю, что именно здесь вы запутались. Думайте об этом скорее с точки зрения кучи (или просто «памяти», если хотите) и таблицы символов.

Давайте начнем с первых нескольких строк вашего кода:

var a, b;

a = {}
b = a;

То, что вы здесь сделали, - это создание одного объекта в куче и двух символов в таблице символов. Это выглядит примерно так:


Таблица символов :

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

Heap

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

.


Здесь все становится интереснее: у объектов есть свои собственные "таблицы символов" (обычно это просто хеш-таблицы, но если их назвать таблицей символов, это станет понятнее)

Теперь, после вашего следующего утверждения, вам нужно учесть три вещи: глобальная таблица символов, таблица символов <object val 1> и куча.

Запустите следующую строку:

a['one'] = {}

А теперь все выглядит так:


Глобальная таблица символов :

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|      a |        0x400000 |
+--------+-----------------+
|      b |        0x400000 |
+--------+-----------------+
Таблица символов

<object val 1>

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

Heap

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

.


Теперь вы запустили следующий код:

a = a['one'];

Надеюсь, это должно показаться тривиальным изменением. Результат:


Таблица глобальных символов :

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|      a |        0x400004 |
+--------+-----------------+
|      b |        0x400000 |
+--------+-----------------+
Таблица символов

<object val 1>

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

Heap

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

.


Надеемся, что после размещения памяти в куче должно быть ясно, почему вы получили вывод, который вы сделали.

Теперь все становится еще интереснее, потому что теперь вы делаете:

a['two'] = 2;

Хорошо, давайте рассмотрим шаг за шагом.

  • a указывает на область памяти 0x400004, которая содержит <object val 2>
  • <object val 2> - пустой объект, поэтому его таблица символов начинается с пустого
  • Запустив эту строку, мы добавим переменную 'two' в таблицу символов <object val 2>.

Если вы еще не устали смотреть на эти диаграммы, вы это сделаете. Теперь все выглядит так:


Глобальная таблица символов :

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|      a |        0x400004 |
+--------+-----------------+
|      b |        0x400000 |
+--------+-----------------+
Таблица символов

<object val 1>

+--------+-----------------+
| Symbol | Memory Location |
+--------+-----------------+
|    one |        0x400004 |
+--------+-----------------+
Таблица символов

<object val 2>

+--------+-----------------+
| 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.

.


Если вы усердно потратите время, чтобы проследить места памяти, вы увидите, что ваш браузер отобразил правильный вывод.

8 голосов
/ 30 ноября 2011

Думайте об анонимном объекте как о самом себе, имеющем имя:

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.

Ключ должен помнить, что переменные содержат ссылки на объекты , а не ссылки на другие переменные. И на один и тот же объект может ссылаться любое количество переменных.

6 голосов
/ 30 ноября 2011

Объекты в Javascript могут существовать сами по себе, без имени. Например:

{}

- это новый экземпляр объекта словаря.

a = {};

создает новый объект словаря и заставляет a обращаться к нему. Теперь

b = a;

делает b ссылкой на тот же базовый объект. Затем вы можете указать a точку в другом месте:

a = "hi";

и b по-прежнему указывают на тот же объект словаря, что и раньше. Поведение b не связано с тем, как вы меняете то, на что указывает a.

0 голосов
/ 09 ноября 2014

Насколько я знаю, вы перезаписали a , поэтому я думаю, что двигатель сохраняет его в другом пространстве памяти, тогда как b все еще указывает на старый a 'адрес памяти (который как-то не уничтожается).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...