JavaScript code trick: What's the value of foo.x
foo.x = foo = {n: 2};
determines that foo.x
refers to a property x
of the {n: 1}
object, assigns {n: 2}
to foo
, and assigns the new value of foo
– {n: 2}
– to the property x
of the {n: 1}
object.
The important thing is that the foo
that foo.x
refers to is determined before foo
changes.
See section 11.13.1 of the ES5 spec:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
The assignment operator associates right to left, so you get:
foo.x = (foo = {n: 2})
The left hand side is evaluated before the right hand side.
What is x && foo()?
Both AND and OR operators can shortcut.
So &&
only tries the second expression if the first is true (truth-like, more specifically). The fact that the second operation does stuff (whatever the contents of foo()
does) doesn't matter because it's not executed unless that first expression evaluates to something truthy. If it is truthy, it then will be executed in order to try the second test.
Conversely, if the first expression in an ||
statement is true, the second doesn't get touched. This is done because the whole statement can already be evaluated, the statement will result in true regardless of the outcome of the second expression, so it will be ignored and remain unexecuted.
The cases to watch out for when using shortcuts like this, of course, are the cases with operators where defined variables still evaluate to falsy values (e.g. 0
), and truthy ones (e.g. 'zero'
).
Double-assignment of an object property results in undefined property
foo.x = foo = { n: 2 };
The foo.x
refers to the property x
of the object referred to by foo
. However, foo = { n: 2 }
assigns a completely new object to foo
. x
is indeed assigned to an object, but that object is immediately replaced by another object. The object with the x
property isn’t referenced by anything anymore.
You can read that line as
foo.x = (foo = { n: 2 });
Graphical explanation
var foo = { n: 2 };
foo.x = foo = { n: 2 };
console.log(foo.x);
Assignment associativity
JavaScript evaluates expressions from left to right. We can show what's going on by using an additional variable:
var foo = {};
var bar = foo; // keep a reference to the object originally stored in foo
foo.x = foo = {n: 2};
Because of associativity, the last statement is parsed as:
foo.x = (foo = {n: 2});
But because of evaluation order, foo.x
runs first (determining where to store the value), then foo
, then {n: 2}
. So we store {n: 2}
in the variable foo
, then assign the same value to the property x
of the old contents of foo
... which we can see by looking at bar
:
foo = {"n" : 2}
bar = {"x" : {"n" : 2 }}
Assignment associativity
JavaScript evaluates expressions from left to right. We can show what's going on by using an additional variable:
var foo = {};
var bar = foo; // keep a reference to the object originally stored in foo
foo.x = foo = {n: 2};
Because of associativity, the last statement is parsed as:
foo.x = (foo = {n: 2});
But because of evaluation order, foo.x
runs first (determining where to store the value), then foo
, then {n: 2}
. So we store {n: 2}
in the variable foo
, then assign the same value to the property x
of the old contents of foo
... which we can see by looking at bar
:
foo = {"n" : 2}
bar = {"x" : {"n" : 2 }}
Why does this code create the object, but still treats it as undefined?
I was wrong. This should throw an exception even before evaluating the rhs. It's a bug (or at least, deviation from the spec) in the engine. See https://es.discourse.group/t/rhs-evaluation-in-assignment-to-unresolvable-reference/310.
That's just how it works:
- The left-hand side is evaluated to a reference
- The right-hand side is evaluated to a value
- The value is put in the reference, which throws an error if the reference cannot resolve its base value
I don't know why they didn't bother checking the reference before evaluating the right-hand side, however this behaviour is consistent with a setter property throwing.
Related Topics
Accessing ASP.NET Controls Using Jquery (All Options)
How to Print an Iframe from JavaScript in Safari/Chrome
Simple Function to Sort an Array of Objects
Angular4 - No Value Accessor for Form Control
Angular 2 Dependency Injection in Es5 and Es6
Jquery Clone() Not Cloning Event Bindings, Even with On()
How to Print Part of a Rendered HTML Page in JavaScript
How to Read Console Logs of Wkwebview Programmatically
Implement JavaScript Alert and Confirm on Wkuidelegate Swiftui
What Is Wkerrordomain Error 4 from Wkwebview
How to Add/Remove a Class in JavaScript
Rendering React Components with Promises Inside the Render Method
How to Export Tables to Excel from a Webpage
What Is the Fastest Factorial Function in JavaScript
Drawing a Line with Three.Js Dynamically
Access Parent's Parent from JavaScript Object
Angular 4.3.3 Httpclient:How Get Value from the Header of a Response