Подтвердить что ты не робот

Javascript указатель/безумное обращение. Может кто-нибудь объяснить это?

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.

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

var a, b;

a = {}
b = a;

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


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

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

Heap:

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

.


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

Теперь, после вашего следующего утверждения, у вас есть 3 вещи для рассмотрения: глобальная таблица символов, таблица символов <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.

.


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

Ответ 3

Подумайте об анонимном объекте, так как он сам имеет имя:

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.

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

Ответ 4

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

{}

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

a = {};

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

b = a;

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

a = "hi";

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

Ответ 5

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