Очередность присвоения и смена ссылок на объекта

Встретил интересную задачу.

Задача: Чему будет равно foо.x?

var foo = {n: 1};
var bar = foo;
foo.x = foo = {n: 2};

Ответ: undefined. Насколько я понимаю, foo.x, на последней строке, ссылается на старую ссылку foo, при этом, присваивая свойству x значение новой ссылки foo. Как в этом случае работает присвоение и замена ссылки для foo?


Ответы (2 шт):

Автор решения: Grundy

Согласно спецификации:

  1. выражение foo.x = foo = {n: 2}; рассматривается как foo.x = (foo = {n: 2});
  2. при выполнении присваивания leftExpression = expressin:
    1. сначала вычисляется leftExpression, для получения места куда присваивать
    2. затем вычисляется expressin
    3. выполняется непосредственно присваивание.

Для примера в вопросе

  1. "вычисляется" foo.x - в данном случае foo это объект сохраненный в bar
  2. "вычисляется" foo = {n: 2}
    1. вычисляется foo - в данном случае это объект сохраненный в bar
    2. вычисляется {n: 2}
    3. в переменную foo присваивается значения из 2.1
  3. в сохраненную foo в поле x присваивается значение вычисленное в пункте 2

Таким образом, ссылка на новый foo доступна через переменную bar

var foo = {
  n: 1
};
var bar = foo;
foo.x = foo = {
  n: 2
};

console.log(foo);
console.log(bar)

→ Ссылка
Автор решения: Павел Сергиенко

Вот объяснение, которое я нашел для себя.

foo.x = (foo = {n: 2});
  1. На первом этапе вычисляется leftExpression, а это foo.x

leftExpression = expression

  1. Далее вычисляется права часть expression. Для этого выражения foo становится ссылкой на новый объект (foo = {n: 2})

  2. Вычисления из первого пункта становятся недействительны. Поскольку, чтобы в foo.x было что-то записано, нужно чтобы ссылка на объект существовала, а она только что была изменена при этом присвоении: foo = {n: 2}.

  3. Исходя из пункта 3: смотрим foo.x выдается undefined, как и должно было.

  4. При этом во время присвоения foo.x эта же ссылка все еще сохранена в bar, поэтому в bar будет создано новое свойство x со значением в виде ссылки на объект {n: 2}. В результате чего bar.x == foo выдаст true (одна и та же ссылка на объект)

→ Ссылка