A ordem das operações é mais clara quando você explora o operador vírgula dentro da notação de colchetes para ver quais partes são executadas quando:
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
Olhando para as especificações :
A produção AssignmentExpression : LeftHandSideExpression = AssignmentExpression
é avaliada da seguinte forma:
Seja lref o resultado da avaliação de LeftHandSideExpression.
Seja rref o resultado da avaliação de AssignmentExpression.
Deixe rval ser GetValue(rref)
.
Lança uma exceção SyntaxError se ... (irrelevante)
Ligue PutValue(lref, rval)
.
PutValue
é o que lança o TypeError
:
Deixe O ser ToObject(base)
.
Se o resultado da chamada do [[CanPut]]
método interno de O com o argumento P for falso, então
uma. Se Throw for true, lance uma exceção TypeError.
Nada pode ser atribuído a uma propriedade de undefined
- o [[CanPut]]
método interno de undefined
sempre retornará false
.
Em outras palavras: o intérprete analisa o lado esquerdo, a seguir analisa o lado direito e, a seguir , lança um erro se a propriedade do lado esquerdo não puder ser atribuída.
Quando você faz
a.x.y = b.e = 1
O lado esquerdo é analisado com sucesso até que PutValue
seja chamado; o fato de a .x
propriedade ser avaliada como undefined
não é considerado até que o lado direito seja analisado. O intérprete vê isso como "Atribuir algum valor à propriedade" y "de undefined", e atribuir a uma propriedade undefined
apenas joga dentro PutValue
.
Em contraste:
a.x.y.z = b.e = 1
O intérprete nunca chega ao ponto em que tenta atribuir à z
propriedade, porque primeiro deve resolver a.x.y
para um valor. Se a.x.y
resolvido para um valor (mesmo para undefined
), estaria tudo bem - um erro seria lançado dentro PutValue
como acima. Mas o acesso a.x.y
gera um erro, porque a propriedade y
não pode ser acessada em undefined
.
b.z = 1
eb.e = 1
executaria primeiro (dada a associatividade à direita ativada=
), depoisa.x.y.z = ...
executaria e falharia; por que ab
atribuição é aprovada em um caso, mas não no outro?