This article covers an advanced topic, to understand certain edge-cases better.
It’s not important. Many experienced developers live fine without knowing it. Read on if you want to know how things work under the hood.
A dynamically evaluated method call can lose
On the last line there is a conditional operator that chooses either
user.bye. In this case the result is
Then the method is immediately called with parentheses
(). But it doesn’t work correctly!
As you can see, the call results in an error, because the value of
"this" inside the call becomes
This works (object dot method):
This doesn’t (evaluated method):
(user.name == "John" ? user.hi : user.bye)(); // Error!
Why? If we want to understand why it happens, let’s get under the hood of how
obj.method() call works.
Looking closely, we may notice two operations in
- First, the dot
'.'retrieves the property
- Then parentheses
So, how does the information about
this get passed from the first part to the second one?
If we put these operations on separate lines, then
this will be lost for sure:
hi = user.hi puts the function into the variable, and then on the last line it is completely standalone, and so there’s no
'.' returns not a function, but a value of the special Reference Type.
The Reference Type is a “specification type”. We can’t explicitly use it, but it is used internally by the language.
The value of Reference Type is a three-value combination
(base, name, strict), where:
baseis the object.
nameis the property name.
strictis true if
use strictis in effect.
The result of a property access
user.hi is not a function, but a value of Reference Type. For
user.hi in strict mode it is:
// Reference Type value (user, "hi", true)
() are called on the Reference Type, they receive the full information about the object and its method, and can set the right
user in this case).
Reference type is a special “intermediary” internal type, with the purpose to pass information from dot
. to calling parentheses
Any other operation like assignment
hi = user.hi discards the reference type as a whole, takes the value of
user.hi (a function) and passes it on. So any further operation “loses”
So, as the result, the value of
this is only passed the right way if the function is called directly using a dot
obj.method() or square brackets
obj['method']() syntax (they do the same here). There are various ways to solve this problem such as func.bind().
Reference Type is an internal type of the language.
Reading a property, such as with dot
obj.method() returns not exactly the property value, but a special “reference type” value that stores both the property value and the object it was taken from.
That’s for the subsequent method call
() to get the object and set
this to it.
For all other operations, the reference type automatically becomes the property value (a function in our case).
The whole mechanics is hidden from our eyes. It only matters in subtle cases, such as when a method is obtained dynamically from the object, using an expression.