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,
}
Looks completely normal.
Then I called it:
await this.OPTIONS[choice]()
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
thissupposed 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
thisis?
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
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]()
That function gets called like a normal function, not like:
this.getAllTasks()
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')
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),
}
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()
}
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
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(),
}
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))
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)
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)