DEV Community

Cover image for The Day "this" Betrayed Me in JavaScript
Yashwanth Reddy Boreddy
Yashwanth Reddy Boreddy

Posted on

The Day "this" Betrayed Me in JavaScript

This is one of those things that looks small… but quietly breaks everything you thought was working.

Not gonna lie, this wasn’t an instant “I get it” moment. It was confusion first, then frustration, and then clarity. Somewhere in between, something about how I see JavaScript changed.


Where This Even Started

I was building a CLI-based task manager.

Nothing crazy or complex. Server side was fine. On the client side, I decided to structure things properly:

  • A class
  • Methods
  • API calls
  • A menu-driven system

Everything looked clean. Everything felt right.
Until it didn’t.


The Problem That Made No Sense

I had something like this:

this.OPTIONS = {
    1: this.getAllTasks,
    2: this.getTaskByPriority,
    3: this.createTask,
}
Enter fullscreen mode Exit fullscreen mode

Looks completely normal.

Then I called it:

await this.OPTIONS[choice]()
Enter fullscreen mode Exit fullscreen mode

And suddenly, this was undefined… or worse, it pointed somewhere random.

That’s where it got frustrating. This was inside a class, so why was it behaving like it wasn’t?


Where My Thinking Went Wrong

I started questioning everything:

  • Isn’t this supposed to refer to the object?
  • Is this some class related issue?
  • Is JavaScript being weird because it’s loosely typed?
  • Why do I have to tell it what this is?

And the worst part is, JavaScript doesn’t fail loudly here. It just quietly does the wrong thing.


The Real Problem (The Thing I Missed)

The issue wasn’t classes, OOP or Syntax.

It was this one rule:

In JavaScript, this depends on how a function is called not where it is written.

That’s it. That’s the whole game.
That single idea explains everything.


What Was Actually Happening

When I wrote this:

1: this.getAllTasks
Enter fullscreen mode Exit fullscreen mode

I thought I was “storing a method”. But I wasn’t. I was extracting a function… a raw function reference, detached from the object.

Later, when I did:

this.OPTIONS[choice]()
Enter fullscreen mode Exit fullscreen mode

That function gets called like a normal function, not like:

this.getAllTasks()
Enter fullscreen mode Exit fullscreen mode

So JavaScript had no context for this.

In strict mode, it becomes undefined. Otherwise, it falls back to the global object. Either way, it breaks anything that depends on instance properties like:

const data = await this.api.get('/tasks')
Enter fullscreen mode Exit fullscreen mode

The Fix That Felt Weird at First

Then came this:

this.OPTIONS = {
    1: this.getAllTasks.bind(this),
    2: this.getTaskByPriority.bind(this),
    3: this.createTask.bind(this),
}
Enter fullscreen mode Exit fullscreen mode

At first, it felt unnecessary. Why bind something that already belongs to the class?

But .bind(this) creates a new function and permanently locks it's thisto the current instance. No matter where that function is called later, it won’t lose it's context.


What .bind(this) Really Means

Without bind:
The function forgets who owns it.

With bind:
The function carries it's owner everywhere.

That’s it. That’s the difference.


The Part Where It Actually Matters

This is where everything clicked:

async start() {
    this.OPTIONS = {
        1: this.getAllTasks.bind(this),
        2: this.getTaskByPriority.bind(this),
        3: this.createTask.bind(this),
        4: this.updateTask.bind(this),
        5: this.deleteTask.bind(this),
    }

    const choice = getUserChoice()

    if (!this.OPTIONS[choice]) {
        console.log('Invalid choice')
        return await this.start()
    }

    await this.OPTIONS[choice]()
    await this.start()
}
Enter fullscreen mode Exit fullscreen mode

Same structure, same logic but now it works perfectly.
The only difference is that the context is no longer lost.


Why JavaScript Works Like This

This started making more sense when I looked at how JavaScript is designed.

It’s function-first and highly flexible. Functions are just values you can pass them around freely, store them, and call them anywhere.

But they don’t carry context automatically.

Other languages tie methods to objects at definition time. JavaScript doesn’t. Here, context is decided at call time.


The Connection I Noticed

This reminded me of something else in JavaScript:

3 == '3'   // true
3 === '3'  // false
Enter fullscreen mode Exit fullscreen mode

At first, it feels inconsistent. But then the pattern is the same… JavaScript gives flexibility first and lets you opt into strictness

Same idea here:

  • Functions are free by default
  • Context is optional
  • You decide when to lock it (bind)

Another Way to Fix It (That Felt More Natural)

Instead of bind, I also tried this:

this.OPTIONS = {
    1: () => this.getAllTasks(),
    2: () => this.getTaskByPriority(),
    3: () => this.createTask(),
}
Enter fullscreen mode Exit fullscreen mode

This worked too. Because arrow functions don’t have their own this. They inherit it from where they are defined.

I had actually used this pattern before in event listeners:

button.addEventListener('click', (event) => handleClick(event))
Enter fullscreen mode Exit fullscreen mode

At that time, I wasn’t thinking about this. Now it makes sense why it worked.


The Bigger Realization

This wasn’t really about .bind().

It was about understanding how JavaScript thinks.

I assumed methods stay attached to objects. But in JavaScript, functions don’t belong to anything unless you explicitly bind them.

That was the mental shift.


One Subtle Thing I Learned

.bind(this) does not modify the original function.

It creates a new one.

So this:

this.getAllTasks !== this.getAllTasks.bind(this)
Enter fullscreen mode Exit fullscreen mode

They are different functions.

This matters if you ever compare functions or try to remove event listeners.


Final Thought

This didn’t make me feel smart. It made me feel confused, then frustrated, and finally clear.
And honestly, that’s progress.

If you’re stuck on this and .bind(), you’re not alone. It doesn’t click immediately, but once it does… you stop fighting JavaScript and start understanding it.


I’m still new to this, but this one definitely changed how I look at the language.
And yeah, that’s another shell cracked.

Top comments (0)