var a = {}
var b = {}
try{
a.x.y = b.e = 1 // Uncaught TypeError: Cannot set property 'y' of undefined
} catch(err) {
console.error(err);
}
console.log(b.e) // 1
var a = {}
var b = {}
try {
a.x.y.z = b.e = 1 // Uncaught TypeError: Cannot read property 'y' of undefined
} catch(err) {
console.error(err);
}
console.log(b.e) // undefined
En fait, si vous lisez correctement le message d'erreur, les cas 1 et 2 génèrent des erreurs différentes.
Cas a.x.y
:
Impossible de définir la propriété 'y' de non défini
Cas a.x.y.z
:
Impossible de lire la propriété 'y' de non défini
Je suppose qu'il est préférable de le décrire par une exécution étape par étape dans un anglais facile.
Cas 1
// 1. Declare variable `a`
// 2. Define variable `a` as {}
var a = {}
// 1. Declare variable `b`
// 2. Define variable `b` as {}
var b = {}
try {
/**
* 1. Read `a`, gets {}
* 2. Read `a.x`, gets undefined
* 3. Read `b`, gets {}
* 4. Set `b.z` to 1, returns 1
* 5. Set `a.x.y` to return value of `b.z = 1`
* 6. Throws "Cannot **set** property 'y' of undefined"
*/
a.x.y = b.z = 1
} catch(e){
console.error(e.message)
} finally {
console.log(b.z)
}
Cas 2
// 1. Declare variable `a`
// 2. Define variable `a` as {}
var a = {}
// 1. Declare variable `b`
// 2. Define variable `b` as {}
var b = {}
try {
/**
* 1. Read `a`, gets {}
* 2. Read `a.x`, gets undefined
* 3. Read `a.x.y`, throws "Cannot **read** property 'y' of undefined".
*/
a.x.y.z = b.z = 1
} catch(e){
console.error(e.message)
} finally {
console.log(b.z)
}
Dans les commentaires, Solomon Tam trouvé cette documentation ECMA sur l'opération d'affectation .
L'ordre des opérations est plus clair lorsque vous exploitez l'opérateur virgule dans la notation entre crochets pour voir quelles parties sont exécutées lorsque:
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
En regardant le spec :
La production
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
Est évaluée comme suit:
Soit lref le résultat de l'évaluation de LeftHandSideExpression.
Soit rref le résultat de l'évaluation de AssignmentExpression.
Soit rval
GetValue(rref)
.Lance une exception SyntaxError si ... (non pertinent)
Appelez
PutValue(lref, rval)
.
PutValue
est ce qui jette le TypeError
:
Soit O soit
ToObject(base)
.Si le résultat de l'appel de la méthode interne
[[CanPut]]
De O avec l'argument P est faux, alorsune. Si Throw est vrai, lancez une exception TypeError.
Rien ne peut être attribué à une propriété de undefined
- la méthode interne [[CanPut]]
De undefined
renverra toujours false
.
En d'autres termes: l'interpréteur analyse le côté gauche, puis analyse le côté droit, alors renvoie une erreur si la propriété du côté gauche ne peut pas être affectée à.
Quand tu fais
a.x.y = b.e = 1
Le côté gauche est analysé avec succès jusqu'à ce que PutValue
soit appelé; le fait que la propriété .x
soit évaluée à undefined
n'est pris en compte qu'après l'analyse du côté droit. L'interpréteur le voit comme "Attribuer une valeur à la propriété" y "d'undefined", et l'affecter à une propriété de undefined
ne lance qu'à l'intérieur de PutValue
.
En revanche:
a.x.y.z = b.e = 1
L'interpréteur n'arrive jamais au point où il essaie d'affecter à la propriété z
, car il doit d'abord résoudre a.x.y
En une valeur. Si a.x.y
Était résolu en une valeur (même en undefined
), ce serait OK - une erreur serait lancée à l'intérieur de PutValue
comme ci-dessus. Mais accèsa.x.y
Génère une erreur, car la propriété y
n'est pas accessible sur undefined
.
Considérez le code suivant:
var a = {};
a.x.y = console.log("evaluating right hand side"), 1;
Les grandes lignes des étapes nécessaires à l'exécution du code sont les suivantes ref:
y
de non défini sur la valeur. Ceci est censé lever une exception TypeError ref.