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
.
Указывает ли пустой объект {}
на какое-либо место в памяти, поэтому каждая переменная, ссылающаяся на него, теперь указывает на одно и то же место?
Может ли кто-то с твердым пониманием объяснить это мне?
Ответы
Ответ 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 > адрес памяти (который каким-то образом не разрушается).