DEV Community

Cover image for The Secret Life of JavaScript: Identity
Aaron Rose
Aaron Rose

Posted on

The Secret Life of JavaScript: Identity

Why this is undefined. A visual guide to the "Left of the Dot" rule


Timothy slumped into a chair at the main worktable, dropping his pen onto a piece of code. He looked exhausted.

"I don't understand who I am anymore, Margaret," he muttered.

Margaret paused her sorting and walked over. "That is a deep philosophical question, Timothy."

"It’s not philosophy. It’s this function," he said, tapping the paper. "I wrote a printName function inside my user object. When I run it, it prints 'Timothy'. But when I pass that exact same function to a helper, it forgets who it is. It prints undefined. It’s having an identity crisis."

Margaret pulled a rolling chalkboard over to the table. She picked up a piece of chalk.

"The function is not having a crisis," she said. "You are simply assuming that Identity (this) belongs to the function. It does not."

The Rule of the Dot

She drew a large function on the board.

"In JavaScript, the word this is not a fixed label," Margaret explained. "It is a question. When the code runs, the function looks around and asks: 'Who called me?'"

She wrote down Timothy's example, drawing a thick arrow under the code.

const user = {
    name: "Timothy",
    speak: function() {
        console.log("My name is " + this.name);
    }
};

user.speak(); 
//  ^ Look to the left
// The object 'user' is calling the function.
// Therefore: 'this' is 'user'.

Enter fullscreen mode Exit fullscreen mode

"Look at the last line," Margaret said, pointing to the dot. "The rule is simple: Look to the Left of the Dot."

"The word user is there," Timothy said.

"Exactly. Because you called it through the user, the function answers the question 'Who called me?' with 'The User.'"

The Loss of Context

"But here is where I failed," Timothy said. He wrote his bug on the board.

const myFunction = user.speak;

myFunction(); 
//  ^ Look to the left
// There is no dot. There is no object.
// Output: "My name is undefined"

Enter fullscreen mode Exit fullscreen mode

"I didn't change the code inside!" Timothy argued. "It's the same function!"

"The code inside didn't change," Margaret agreed. "But the Call Site did."

"Look to the left of myFunction()," she instructed. "Is there a dot? Is there an object?"

Timothy looked. "No. It's just the function name."

"Precisely," Margaret said. "When there is no dot, the function has no owner. In strict mode—which we always use—this becomes undefined."

"And in the old days?"

"In the old days," Margaret shuddered, "it would default to the Global Window. A recipe for disaster."

Forcing the Issue (call & bind)

"So this is fragile," Timothy realized. "It depends entirely on how I call the function, not where I wrote it."

"Correct," Margaret said. "But you can force it."

She wrote two final examples on the board.

1. The One-Time Call

const stranger = { name: "Margaret" };

// We force 'speak' to use 'stranger' as 'this' right now
user.speak.call(stranger);
// Output: "My name is Margaret"

Enter fullscreen mode Exit fullscreen mode

"With .call()," she explained, "you are telling the function: 'I don't care where you are. For this one specific execution, your identity is this object.'"

2. The Permanent Copy

"But what if I want to pass the function around?" Timothy asked. "Like to a click handler?"

"Then you need a permanent seal," Margaret said. "You need .bind()."

// We create a NEW function that is permanently locked to 'user'
const boundFunction = user.speak.bind(user);

boundFunction();
// Output: "My name is Timothy" (Forever)

Enter fullscreen mode Exit fullscreen mode

".bind() does not run the function," she noted. "It returns a new copy of the function that remembers its owner forever. No matter how you call it later, this will always be user."

The Conclusion

Timothy looked at the chalkboard. The rules were simple, but strict.

  1. Is there a dot? -> this is the object on the left.
  2. No dot? -> this is undefined (in strict mode).
  3. Did you use .call() or .bind()? -> this is what you said it was.

"I thought this was about where the function lived," Timothy admitted.

"That is a common mistake," Margaret said, dusting the chalk from her hands. "In JavaScript, identity is not about who you are. It is about who is holding you at the moment you speak."


Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.

Top comments (2)

Collapse
 
peacebinflow profile image
PEACEBINFLOW

This is such a clean explanation of this — the “left of the dot” rule is the only mental model that doesn’t betray you at 2am.

What I like is you didn’t turn it into folklore (“this is weird lol”). You pinned it to the one thing that matters: the call site. Same function body, different caller, different identity — that’s the whole bug class right there.

The call() vs bind() split is also perfect: call() is “borrow this identity once,” bind() is “mint a new function with identity sealed.” That distinction saves people from doing cursed things like rebinding in loops or wondering why handlers lose context.

If someone reads just this chapter and nothing else, they’ll stop blaming JavaScript and start reading the code the way the runtime actually executes it. That’s a win.

Collapse
 
aaron_rose_0787cc8b4775a0 profile image
Aaron Rose

Thanks and cheers 💯✨💪