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
実際、エラーメッセージを適切に読んだ場合、ケース1とケース2は異なるエラーをスローします。
ケースa.x.y
:
未定義のプロパティ「y」をsetできません
ケースa.x.y.z
:
未定義のプロパティ「y」をreadできません
簡単な英語で段階的に実行して説明するのが最善だと思います。
ケース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)
}
ケース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)
}
コメントでは、 Solomon Tam found このECMAの割り当て操作に関するドキュメント 。
ブラケット表記内のカンマ演算子を利用して、次の場合に実行される部分を確認すると、操作の順序が明確になります。
_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
_
spec を見てください:
実動_
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
_は次のように評価されます:
LrefをLeftHandSideExpressionの評価結果とします。
RrefをAssignmentExpressionの評価結果とします。
Rvalを
GetValue(rref)
とします。次の場合にSyntaxError例外をスローします(無関係)
PutValue(lref, rval)
を呼び出します。
PutValue
は、TypeError
をスローするものです。
Oを
ToObject(base)
とする。引数PでOの_
[[CanPut]]
_内部メソッドを呼び出した結果がfalseの場合、a。 Throwがtrueの場合、TypeError例外をスローします。
undefined
のプロパティには何も割り当てることができません。undefined
の_[[CanPut]]
_内部メソッドは常にfalse
を返します。
つまり、インタープリターは左側を解析し、右側を解析します。左側のプロパティを割り当てることができない場合、 then はエラーをスローしますに。
するとき
_a.x.y = b.e = 1
_
PutValue
が呼び出されるまで、左側はが正常に解析されましたです。 _.x
_プロパティがundefined
と評価されるという事実は、右側が解析されるまで考慮されません。インタプリタは、「未定義のプロパティ「y」に値を割り当てる」と見なし、undefined
のプロパティに割り当てると、PutValue
内でのみスローされます。
対照的に:
_a.x.y.z = b.e = 1
_
インタプリタは、最初に_a.x.y
_を値に解決する必要があるため、z
プロパティに割り当てようとするポイントに到達することはありません。 _a.x.y
_が値(undefined
にさえ)に解決された場合、問題ありません-上記のようにPutValue
内でエラーがスローされます。ただし、 accessing _a.x.y
_は、y
でプロパティundefined
にアクセスできないため、エラーをスローします。
次のコードを検討してください。
var a = {};
a.x.y = console.log("evaluating right hand side"), 1;
コードを実行するために必要な手順の大まかな概要は次のとおりです。 参照: