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
Actually, if you read the error message properly, case 1 and case 2 throw different errors.
Case a.x.y
:
Cannot set property 'y' of undefined
Case a.x.y.z
:
Cannot read property 'y' of undefined
I guess it's best to describe it by step-by-step execution in easy English.
Case 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) }
Case 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) }
In comments, Solomon Tam found this ECMA documentation about assignment operation.
The order of operations is clearer when you exploit the comma operator inside bracket notation to see which parts are executed when:
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
Looking at the spec:
The production
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
is evaluated as follows:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
Let rval be
GetValue(rref)
.Throw a SyntaxError exception if... (irrelevant)
Call
PutValue(lref, rval)
.
PutValue
is what throws the TypeError
:
Let O be
ToObject(base)
.If the result of calling the
[[CanPut]]
internal method of O with argument P is false, thena. If Throw is true, then throw a TypeError exception.
Nothing can be assigned to a property of undefined
- the [[CanPut]]
internal method of undefined
will always return false
.
In other words: the interpreter parses the left-hand side, then parses the right-hand side, then throws an error if the property on the left-hand side can't be assigned to.
When you do
a.x.y = b.e = 1
The left hand side is successfully parsed up until PutValue
is called; the fact that the .x
property evaluates to undefined
is not considered until after the right-hand side is parsed. The interpreter sees it as "Assign some value to the property "y" of undefined", and assigning to a property of undefined
only throws inside PutValue
.
In contrast:
a.x.y.z = b.e = 1
The interpreter never gets to the point where it tries to assign to the z
property, because it first must resolve a.x.y
to a value. If a.x.y
resolved to a value (even to undefined
), it would be OK - an error would be thrown inside PutValue
like above. But accessing a.x.y
throws an error, because property y
cannot be accessed on undefined
.
Consider the following code:
var a = {};
a.x.y = console.log("evaluating right hand side"), 1;
The rough outline of steps required to execute the code is as follows ref:
a.x.y
returns a reference ref consisting of base value a.x
(undefined) and referenced name (y
).y
of undefined to the value. This is supposed to throw a TypeError exception ref.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With