Многократная путаница заданий

Я понимаю, что оператор присваивания является правильным ассоциативным.

Итак, например, x = y = z = 2 эквивалентно (x = (y = (z = 2)))

В этом случае я попробовал следующее:

foo.x = foo = {a:1}

Я ожидал, что объект foo будет создан со значением {a:1}, а затем свойство x будет создано на foo, которое будет просто ссылкой на объект foo.

(На самом деле это происходит, если я должен был отделить оператор множественного присваивания от двух отдельных операторов foo = {a:1};foo.x = foo;)

Результат был фактически:

ReferenceError: foo не определен (...)

Итак, я попробовал следующее:

var foo = {};
foo.x = foo = {a:1};

Теперь я больше не получаю исключение, но foo.x есть undefined!

Почему назначение не работает так, как я ожидал?


Отказ от ответственности. Вопрос "дубликат" кажется очень отличным от того, который я задаю, поскольку проблема заключается в том, что переменные, которые были созданы в присваивании, были глобальными, поскольку они были связаны с созданными переменными с ключевым словом var. Это не проблема.

Ответы

Ответ 1

Существует важное различие между ассоциативностью и порядком оценки.

В JavaScript, хотя оператор присваивания имеет право справа налево, операнды оцениваются слева направо до того, как выполняются фактические назначения (которые происходят справа налево). Рассмотрим этот пример:

var a = {};
var b = {};
var c = a;

c.x = (function() { c = b; return 1; })();

Переменная c первоначально ссылается на a, но правая часть присваивания устанавливает c в b. Какое свойство присваивается, a.x или b.x? Ответ a.x, потому что левая сторона сначала оценивается, когда c все еще ссылается на a.

В общем случае выражение x = y оценивается следующим образом:

  • Оцените x и запомните результат.
  • Оцените y и запомните результат.
  • Назначить результат с шага 2 на результат шага 1 (и вернуть его в результате выражения x = y).

Что происходит с несколькими назначениями, как в x = (y = z)? Рекурсия!

  • Оцените x и запомните результат.
  • Оцените y = z и запомните результат. Сделать это:
    • Оцените y и запомните результат.
    • Оцените z и запомните результат.
    • Назначить результат с шага 2.2 для результата этапа 2.1 (и вернуть его в результате выражения y = z).
  • Назначить результат с шага 2 на результат шага 1 (и вернуть его в результате выражения x = (y = z)).

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

var foo = {};
var bar = foo;         // save a reference to foo
foo.x = (foo = {a:1}); // add parentheses for clarity

foo.x оценивается до foo присваивается {a:1}, поэтому свойство x добавляется к исходному объекту {} (который вы можете проверить, изучив bar).

Ответ 2

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

Прежде всего, вы должны понимать разницу между Reference - и Value -.

var foo = {};

foo переменная содержит ссылку на объект в памяти, скажем A

Теперь есть два вида аксессуаров: Variable Accessor и Property Accessor.

So foo.x = foo = {a:1} можно понимать как

[foo_VARIABLE_ACCESSOR][x_PROPERTY_ACCESSOR] = [foo_VARIABLE_ACCESSOR] = {a:1}

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

A['x'] = foo = {a:1}

Аксессор свойств разделен на сеттеры и геттеры

var foo = { bar: {} };
foo.bar.x = foo = {a:1}

Здесь где деканированы два вложенных объекта foo и bar. В памяти у нас есть два объекта A и B.

[foo_VAR_ACCESSOR][bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}

> A[bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B[x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B['x'] = foo = {a: 1}

Здесь у вас мало примеров

var A = {};
var B = {}
Object.defineProperty(A, 'bar', {
    get () {
        console.log('A.bar::getter')
        return B;
    }
})
Object.defineProperty(B, 'x', {
    set () {
        console.log('B.x::getter')
    }
});

var foo = A;
foo.bar.x = foo = (console.log('test'), 'hello');

// > A.bar.getter
// > test
// > B.x.setter

Ответ 3

Отличный вопрос. Здесь нужно помнить, что JavaScript использует указатели для всего. Легко забыть об этом, так как невозможно получить доступ к значениям, представляющим адреса памяти в JavaScript (см. этот вопрос SO). Но осознание этого очень важно для понимания многих вещей в JavaScript.

Итак, утверждение

var foo = {};

создает объект в памяти и присваивает указатель этому объекту foo. Теперь, когда этот оператор работает:

foo.x = foo = {a: 1};

свойство x фактически добавляется к исходному объекту в памяти, а foo получает назначенный указатель на новый объект, {a: 1}. Например,

var foo, bar = foo = {};
foo.x = foo = {a: 1};

показывает, что если foo и bar сначала указывают на один и тот же объект, bar (который все равно укажет на этот исходный объект) будет выглядеть как {x: {a: 1}}, а foo - просто {a: 1}.

Так почему же foo выглядит как {a: 1, x: foo}?

В то время как вы правы в том, что присвоения являются правильными ассоциативными, вы также должны понимать, что интерпретатор все еще читает слева направо. Возьмем подробный пример (с некоторыми абзацами):

var foo = {};

Хорошо, создайте объект в ячейке памяти 47328 (или что-то еще), присвойте foo указателю, указывающему на 47328.

foo.x = ....

Хорошо, возьмите объект, который foo в настоящее время указывает на в ячейке памяти 47328, добавьте к нему свойство x и приготовьтесь присвоить x ячейке памяти любого следующий следующий.

foo = ....

Хорошо, возьмите указатель foo и приготовьтесь назначить его в ячейку памяти следующего следующего.

{a: 1};

Хорошо, создайте новый объект в памяти в местоположении 47452. Теперь верните цепочку: Назначьте foo, чтобы указать на ячейку памяти 47452. Назначьте свойство x объекта в ячейке памяти 47328, чтобы также указать на то, что foo теперь указывает на - ячейку памяти 47452.

Короче говоря, нет сокращенного способа делать

var foo = {a: 1};
foo.x = foo;