The Impact of Mental Models on Learning Programming Languages
Preface
- This article uses JavaScript’s primitive types as examples; their behavior might differ from other programming languages.
- Primitive types behave differently from objects and arrays; only primitive types are used as examples here.
What Is a Mental Model?
A mental model refers to the cognitive process of how we anticipate things unfolding or how things work.
It sounds a bit academic, but for example, when we see a button UI on the screen, we expect it to be clickable and that clicking it might trigger a series of events. Therefore, when users find that the UI does not behave as expected, they feel confused.
Why do we perceive such a UI as a button in the first place? On one hand, it is due to our environment; on the other hand, it is experience. Ever since users first started using the web, they have recognized buttons based on these patterns, so they naturally apply them across other websites as well.
Take this knob, for example. Why is our first instinct to turn it rather than press it? Because in daily life, most round mechanisms are adjusted by turning them, so we apply this rule elsewhere.

Or consider a door handle. Why don’t we try to twist it to open the door, but instead push down on it? It is also because we have a preconceived understanding of how door handles work, leading us to expect the door to open when we press down.

When learning a programming language, we also develop mental models of how the language works as our learning progresses, mentally compiling and anticipating how code will execute.
The Importance of Mental Models
Let’s use some JavaScript code as an example.
let a = 3;
let b = a + 3;
a += 1;
console.log(b); // b
This is very basic JavaScript code. Experienced developers already know what the answer is and where the catch lies. If we depict this code using an incorrect mental model, we might end up with something like this:
1. Before a += 1 is executed:

2. After a += 1 is executed:

Naturally, because a had 1 added to it, we change the 3 inside the circle to 4.
And because b = a + 3, since a has changed, the circle pointed to by b should also change to 7. Therefore, those with an incorrect understanding will naturally answer: “7”.
I want to emphasize that although the answer is wrong, I don’t think it is entirely the learner’s fault. Following intuitive reasoning, if someone interprets b = a + 3 like a mathematical equation, then when a += 1 occurs later, b should naturally update along with it.
Once a learner adopts this line of thinking, correcting it down the road becomes exceptionally difficult, and they will frequently encounter bugs they cannot explain.
What Went Wrong?
In JavaScript, you cannot mutate primitive values. This statement seems brief, but for beginners, it is nearly impossible to fundamentally grasp what it actually means.
Here is how it actually works:

Because values of primitive types cannot be altered, we cannot simply change the 3 inside the circle to 4 (3 + 1). Instead, a new number 4 must be created, and the arrow for a points to it. The original 3 remains unchanged (the blue circle), so regardless of any changes to a, the result of b is unaffected.
Rather than citing textbook definitions like “Primitive values in JavaScript are immutable,” I actually prefer explaining it this way. It aids understanding and accurately reflects how JavaScript code operates under the hood. If you look closely, the two models are very similar, but the critical difference lies in whether the learner truly grasps the characteristics and inner workings of JavaScript’s primitive values.
Whether learners can apply this across various scenarios depends on their mental model. If they continually rely on the first mental model, even if they have read that “primitive types are immutable,” they will still easily arrive at the wrong conclusion.
Related Posts
- When a Measure Becomes a Target: From the Window Tax to Pull Request Counts I once wrote a script to tally how many PRs I contributed in a quarter, how many reviews I left, and how many tickets I closed, hoping to use numbers to prove my output to my manager. My manager simply remarked that performance isn't just about output. Years later, I finally understood—when a measure becomes a target, it ceases to be a good measure. From the British window tax and the Hanoi rat bounty to evaluating developers by PR counts today, the underlying mechanism is exactly the same.
- Using Cloudflare Images for Image Storage and Transformation Putting an image on a webpage is the simplest task in frontend development. But doing it properly—including resizing, generating multiple formats, and withstanding heavy traffic—is actually an entire end-to-end solution. Eventually, I offloaded everything to Cloudflare Images, keeping only a single original image.
- Stop Using AWS Access Keys Access Keys are an easily overlooked security risk in AWS. By pairing OIDC with IAM Roles, GitHub Actions can securely operate AWS resources without storing any secrets.
- Database Primary Keys: AUTO_INCREMENT, UUID, and UUIDv7 Backend developers often face the choice of primary keys: should you use auto-increment or UUID? What about collisions? How does UUIDv7 compare to created_at + index in performance? Here are the design decisions and benchmark results from testing 20 million rows.