Reference Type
Here’s a puzzle. A method that works perfectly when you call it the normal way throws an error the moment you pick it dynamically:
let user = {
name: "Maya",
hi() { alert(this.name); },
bye() { alert("Bye"); }
};
user.hi(); // works
// pick user.hi or user.bye depending on the name, then call it
(user.name == "Maya" ? user.hi : user.bye)(); // Error!
Read that last line slowly. The conditional operator (? :) evaluates first and hands back one of the two functions. Since user.name is "Maya", it picks user.hi. Then the trailing () calls whatever came out.
And it breaks. Inside the call, this is undefined, so this.name throws.
Compare the two side by side:
To understand the difference, you have to look at what obj.method() actually does when JavaScript runs it.
Breaking obj.method() into two steps
obj.method() isn’t one atomic operation. It’s two:
- The dot
.reads the propertyobj.method. - The parentheses
()invoke whatever step 1 produced.
The interesting question: how does the information “this call belongs to obj” travel from step 1 to step 2?
You can prove that link is fragile by splitting the two steps across separate lines. Store the method in a variable, then call the variable:
let user = {
name: "Maya",
hi() { alert(this.name); }
};
// getting and calling become two independent statements
let hi = user.hi;
hi(); // Error, because this is undefined
The assignment hi = user.hi copies the function into a plain variable. By the time you write hi(), the call has no idea the function ever lived on user. There’s no receiver, so no this.
Run the two versions yourself. The body of hi is identical both times — only the way it’s called changes, and that’s enough to make one succeed and the other throw:
The Reference Type
So if user.hi on its own is just a function with no attached object, how does user.hi() ever know its this? Here’s the trick JavaScript pulls:
The dot . doesn’t return the plain function. It returns a special value called a Reference Type.
Reference Type is a specification type. It exists inside the language spec to describe internal behavior — you can never read it, print it, or store it in a variable. It only lives for the brief moment between the dot and the call.
Its value is a bundle of three things, written as (base, name, strict):
base— the object the property was read from.name— the property name being accessed.strict—truewhenuse strictis in effect.
Read user.hi in strict mode and, for that instant, the dot produces:
// the Reference Type value (conceptual — you can't see this)
(user, "hi", true)
Now the two-step call makes sense. When () runs on a Reference Type, it has the whole package: the function and its base object. So it sets this to base — user here — and everything works.
That’s the Reference Type’s entire job: a courier that carries this across the gap from the dot to the call.
Why splitting the call loses this
Any operation other than an immediate call unwraps the Reference Type. Assignment, passing as an argument, storing in an array — each one throws away the (base, name, strict) bundle and keeps only the value inside: the plain function.
let hi = user.hi; // the Reference Type is discarded here
hi(); // just a function, no base — this is undefined
That’s exactly what the conditional operator did to us. user.name == "Maya" ? user.hi : user.bye is an expression that evaluates user.hi — and evaluating it collapses the Reference Type down to the plain function. The () that follows never sees user.
Getting this back
Losing this is common enough that the language gives you tools to reattach it. The usual fix is func.bind(), which returns a new function permanently wired to a chosen this:
let hi = user.hi.bind(user);
hi(); // works — bound to user, no matter how it's called
Arrow functions offer another route, since they take this from their surrounding scope instead of the call site. Whichever you reach for, the underlying problem is the same one Reference Type explains: pull a method away from its object and the connection breaks unless you rebuild it.
Here’s the same detached-and-called scenario, run four ways. The first drops this and throws; the other three each rebuild the connection so the call lands on user again:
Summary
Reference Type is an internal, spec-only type — you can’t touch it directly.
When you read a property with the dot, as in obj.method(), the dot hands back more than the property’s value. It returns a small bundle holding both the function and the object it came from, so the following () can set this to that object.
Every other operation — assignment, a conditional, passing the method around — immediately unwraps that bundle into the plain value (the function), and this is gone.
All of this stays invisible during normal coding. It only surfaces in the subtle case where you grab a method dynamically through an expression and then call it. When that call throws a this-related error, Reference Type is the reason.