Порядок операций становится более ясным, когда вы используете оператор запятой внутри скобок, чтобы увидеть, какие части выполняются, когда:
var a = {}
var b = {}
try{
// Uncaught TypeError: Cannot set property 'y' of undefined
a
[console.log('x'), 'x']
[console.log('y'), 'y']
= (console.log('right hand side'), b.e = 1);
} catch(err) {
console.error(err);
}
console.log(b.e) // 1
var a = {}
var b = {}
try {
// Uncaught TypeError: Cannot read property 'y' of undefined
a
[console.log('x'), 'x']
[console.log('y'), 'y']
[console.log('z'), 'z']
= (console.log('right hand side'), b.e = 1);
} catch(err) {
console.error(err);
}
console.log(b.e) // undefined
Смотрим на спецификацию :
Производство AssignmentExpression : LeftHandSideExpression = AssignmentExpression
оценивается следующим образом:
Пусть lref будет результатом вычисления LeftHandSideExpression.
Пусть rref будет результатом вычисления AssignmentExpression.
Пусть будет rval GetValue(rref)
.
Выбрасывать исключение SyntaxError, если ... (неактуально)
Звоните PutValue(lref, rval)
.
PutValue
это то, что бросает TypeError
:
Пусть O будет ToObject(base)
.
Если результат вызова [[CanPut]]
внутреннего метода O с аргументом P ложен, то
а. Если Throw имеет значение true, выбросить исключение TypeError.
Ничто не может быть присвоено свойству undefined
- [[CanPut]]
внутренний метод undefined
всегда будет возвращать false
.
Другими словами: интерпретатор анализирует левую часть, затем анализирует правую часть, а затем выдает ошибку, если свойство в левой части не может быть присвоено.
Когда ты делаешь
a.x.y = b.e = 1
Левая часть успешно анализируется , пока не PutValue
будет вызвана; тот факт, что .x
свойство оценивается как значение undefined
, не рассматривается до тех пор, пока не будет проанализирована правая часть. Интерпретатор видит это как «Присвойте какое-либо значение свойству« y », равному undefined», а присвоение свойству undefined
только значения бросает внутрь PutValue
.
Напротив:
a.x.y.z = b.e = 1
Интерпретатор никогда не доходит до точки, где он пытается назначить z
свойство, потому что сначала он должен разрешить a.x.y
значение. Если a.x.y
разрешить значение (даже до undefined
), все будет в порядке - внутри будет выдана ошибка, PutValue
как указано выше. Но при доступе a.x.y
возникает ошибка, потому что свойство y
недоступно undefined
.
b.z = 1
иb.e = 1
выполнить первое (учитывая правую ассоциативность на=
), а затемa.x.y.z = ...
выполнить и не; почемуb
присвоение выполняется в одном случае, а в другом - нет?