I Learned JavaScript Wrong for 6 Months (Here’s What I Missed)
I could build things.
A to-do app. A calculator. A weather app.
They worked. They looked good.
But I didn’t understand how they worked.
Big difference.
The Moment I Realized Something Was Wrong
I was in a technical interview.
Interviewer: “Explain what happens when you declare a variable with var vs let."
Me: “Um… one is older? And… let is better?"
Interviewer: “Why is it better?"
Me: nervous silence
I had used both a hundred times. But I couldn’t explain the difference.
I knew the syntax. I didn’t know the language.
That interview rejection hit different. It wasn’t about my projects. It was about my understanding.
How I Learned JavaScript Wrong
My learning path looked like this:
Month 1: “Build 5 Projects with JavaScript” tutorial
Month 2: “React for Beginners”
Month 3: “Full Stack JavaScript”
Months 4–6: More project tutorials
Notice what’s missing? Fundamentals.
I jumped straight to building. Skipped the boring theory stuff.
After all, who cares about “hoisting” and “scope” when you can build a cool weather app, right?
Turns out, everyone cares. Especially employers.
What I Skipped (And Paid For Later)
1. How Variables Actually Work
What I knew:
let name = "John";
var age = 25;
const city = "London";Cool. Three ways to make a variable.
What I didn’t know:
- Why does
varleak outside loops, butletdoesn't? - What’s “hoisting”?
- Why can I use a
namebefore declaring it withvarbut notlet? - What’s the “temporal dead zone”?
I had no idea. Just memorized: “Use let and const, avoid var."
2. How Functions Really Work
What I knew:
function sayHi() {
console.log("Hi");
}
const sayBye = () => {
console.log("Bye");
};Two ways to make functions. Both work. Cool.
What I didn’t know:
- What’s the difference between regular functions and arrow functions?
- What is
this,and why does it change? - What’s a closure?
- Why can I call some functions before declaring them but not others?
I just copied whatever the tutorial used.
3. How the DOM Actually Works
What I knew:
document.getElementById('button').addEventListener('click', function() {
// Do stuff
});This adds a click listener. I get it.
What I didn’t know:
- What’s event bubbling?
- What’s event delegation?
- Why does everyone say “don’t manipulate the DOM directly”?
- What’s the difference between
innerHTML,textContent, andinnerText?
I just used whatever worked.
4. Asynchronous JavaScript
What I knew:
fetch(url)
.then(response => response.json())
.then(data => console.log(data));This gets data from an API. Copy-paste from tutorial. Works every time.
What I didn’t know:
- What is a Promise?
- What does “asynchronous” even mean?
- What’s the event loop?
- Why can’t I just
let data = fetch(url)and use it immediately? - What’s
async/await,and how is it different from.then()?
I memorized the pattern. Didn’t understand it.
5. How Objects and Arrays Work
What I knew:
let user = {
name: "John",
age: 25
};
let numbers = [1, 2, 3, 4, 5];Objects store data. Arrays store lists. Easy.
What I didn’t know:
- What’s the difference between
const user = {}and changinguser?name(works) vsuser = {}(error)? - Why does
array1 = array2make them connected? - What are reference types vs primitive types?
- How do
map(),filter(),reduce()actually work?
I used them. Didn’t understand them.
The Tutorial That Exposed My Gaps
I watched a video titled: “JavaScript Concepts Every Developer Should Know.”
15 minutes in, I was lost.
Words I’d never heard:
- Lexical scope
- Closure
- Hoisting
- Call stack
- Event loop
- Prototype chain
- Context binding
Words I’d heard but didn’t understand:
this- Async
- Promise
- Callback
- Constructor
I’d been coding for 6 months and felt like a beginner again.
What I Did to Fix It
I stopped building for 2 weeks.
Instead, I focused on one thing: Understanding how JavaScript actually works.
Here’s what I learned:
Understanding #1: How Variables Actually Work
var vs let vs const
var:
javascript
function test() {
if(true) {
var x = 5;
}
console.log(x); // 5 - works!
}var is function-scoped. Accessible anywhere in the function.
let:
javascript
function test() {
if(true) {
let x = 5;
}
console.log(x); // Error! x is not defined
}let is block-scoped. Only accessible inside that block {}.
Why this matters:
javascript
for(var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000);
}
// Prints: 3, 3, 3 (not 0, 1, 2!)
for(let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000);
}
// Prints: 0, 1, 2 (correct!)With var, all three timeouts share the same i. By the time they run, i = 3.
With let, each iteration has its own i. They remember their own value.
I had run into this bug 5 times and never understood why until now.
Hoisting
This confused me for so long.
With var:
javascript
console.log(name); // undefined (not an error!)
var name = "John";JavaScript “hoists” the declaration to the top:
javascript
var name; // Declared at top
console.log(name); // undefined
name = "John"; // Assigned laterWith let:
javascript
console.log(name); // Error! Cannot access before initialization,
let name = "John";let also hoists, but you can't use it before declaration. This is called the "temporal dead zone."
Why I care: Now I understand why my code sometimes said “undefined” instead of throwing an error.
Understanding #2: Functions and this
The Problem with this
javascript
const user = {
name: "John",
sayHi: function() {
console.log("Hi, I'm " + this.name);
}
};
user.sayHi(); // "Hi, I'm John" - works!
const greet = user.sayHi;
greet(); // "Hi, I'm undefined" - broken!Why? this depends on HOW the function is called, not WHERE it's defined.
user.sayHi()→thisisusergreet()→thisiswindow(or undefined in strict mode)
Arrow Functions Fix This
JavaScript
const user = {
name: "John",
sayHi: () => {
console.log("Hi, I'm " + this.name);
}
};
user.sayHi(); // "Hi, I'm undefined"Wait, that’s worse!
Arrow functions don’t have their own this. They inherit it from where they were defined.
When to use arrow functions:
JavaScript
const user = {
name: "John",
friends: ["Alice", "Bob"],
showFriends: function() {
this. friends.forEach(friend => {
console.log(this.name + " is friends with " + friend);
});
}
};
user.showFriends();
// John is friends with Alice
// John is friends with BobArrow function inside forEach inherits this from showFriends.
If we used a regular function, this would be lost.
This explained SO many bugs I’d encountered.
Understanding #3: Closures (The Mind-Bender)
I’d heard this word 100 times. Never understood it.
Simple explanation:
A function remembers variables from where it was created, even after that place is gone.
JavaScript
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter1 = makeCounter();
console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter1()); // 3
const counter2 = makeCounter();
console.log(counter2()); // 1 (separate counter!)What’s happening:
makeCounter()creates a variablecount- Returns a function that uses
count - Even though
makeCounter()finished running, the returned function still remembers thecount. - Each time we call
makeCounter(), it creates a newcount
This is a closure. The inner function “closes over” the variables from the outer function.
Real-world use:
JavaScript
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15Each function remembers its own multiplier value.
I’d been using closures without knowing what they were.
Understanding #4: How Async Actually Works
This was the biggest gap.
The Problem I Had:
JavaScript
let data;
fetch('https://api.example.com/users')
.then(response => response.json())
.then(result => {
data = result;
});
console.log(data); // undefined - why???I thought fetch pauses the code until data arrives. It doesn't.
How JavaScript Actually Works:
JavaScript is single-threaded. It can only do one thing at a time.
So how does it handle fetch without freezing?
The Event Loop:
- Code runs line by line.
- When it hits
fetch, it says, "Start fetching, I'll check back later." - Continues to next line (doesn’t wait)
- When fetch finishes, it puts the
.then()function in a queue. - When the current code finishes, it checks the queue and runs the
.then()
So:
JavaScript
console.log("1");
fetch(url).then(data => {
console.log("3");
});
console.log("2");
// Output: 1, 2, 3 (not 1, 3, 2!)The Right Way:
JavaScript
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => {
console.log(data); // Use data here
displayUsers(data);
});
// Or with async/await:
async function getUsers() {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
console.log(data); // Now data is here
}This explained why my data was always “undefined” when I tried to use it outside the .then().
Understanding #5: Reference vs Value
Primitive Types (Value):
JavaScript
let a = 5;
let b = a;
a = 10;
console.log(b); // 5 (unchanged)b got a COPY of the value.
Objects and Arrays (Reference):
JavaScript
let user1 = { name: "John" };
let user2 = user1;
user1.name = "Jane";
console.log(user2.name); // "Jane" (changed!)user2 didn't get a copy. It got a REFERENCE pointing to the same object.
Get Saad ullah khan’s stories in your inbox
Join Medium for free to get updates from this writer.
Think of it like:
- Primitive = photocopy (separate)
- Object = shortcut link (connected)
The Bug This Caused:
JavaScript
const original = [1, 2, 3];
const copy = original;
copy.push(4);
console.log(original); // [1, 2, 3, 4] - wait, what?I thought I made a copy. I just created another reference to the same array.
The Fix:
JavaScript
const original = [1, 2, 3];
const copy = [...original]; // Spread operator makes actual copy
copy.push(4);
console.log(original); // [1, 2, 3] - safe!
console.log(copy); // [1, 2, 3, 4]This explained SO many weird bugs where changing one thing changed something else.
Understanding #6: Array Methods
I used map(), filter(), and reduce() because tutorials used them. But I didn't understand HOW they work.
map() — Transform each item
JavaScript
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]What’s happening:
- Loop through each number.
- Run the function on it.
- Put the result in a new array.
Manual version:
JavaScript
const doubled = [];
for(let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}map() is just a cleaner syntax for this.
filter() — Keep only some items
JavaScript
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6]What’s happening
- Loop through each number.
- If the function returns true, keep it.
- If false, skip it
reduce() — Combine into one value
JavaScript
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // 10What’s happening:
- Start with the initial value (0)
- For each number, add it to the total.
- Return final total
Manual version:
JavaScript
let sum = 0;
for(let i = 0; i < numbers.length; i++) {
sum = sum + numbers[i];
}Once I understood these were just fancy loops, they made sense.
Understanding #7: Event Delegation
I always did this:
JavaScript
const buttons = document.querySelectorAll('.button');
buttons.forEach(button => {
button.addEventListener('click', handleClick);
});Works fine for 10 buttons. But what about 1000?
The Problem:
1000 event listeners = slow and memory-heavy.
The Solution: Event Delegation
JavaScript
document.querySelector('.button-container').addEventListener('click', function(event) {
if(event.target.classList.contains('button')) {
handleClick(event);
}
});What’s happening:
Instead of adding a listener to every button, add ONE listener to the container.
When any button is clicked, the click “bubbles up” to the container.
We check which button was clicked using event.target.
1 event listener instead of 1000.
This is how professional websites handle hundreds of interactive elements.
The Learning Resources That Actually Helped
After 6 months of tutorials, I finally went back to fundamentals:
1. JavaScript.info
Free website that explains concepts step by step.
Not “build this project.” Just “here’s how JavaScript works.”
Covered everything I missed:
- Scope and closures
thiskeyword- Prototypes
- Event loop
- Promises
2. “You Don’t Know JS” Book Series
Free on GitHub. Deep dive into how JavaScript actually works.
Chapters I should’ve read first:
- Scope & Closures
- this & Object Prototypes
- Async & Performance
3. MDN Web Docs
The official documentation.
Whenever I used a method, I’d read the full MDN page.
Example: Instead of just using .map(), I read:
- What it does
- What it returns
- What parameters does it takes
- Examples
- Browser support
4. JavaScript Questions Repository (on GitHub)
List of interview questions with explanations.
I went through 50 questions. Couldn’t answer 35 of them.
Then I learned those 35 concepts.
How My Code Changed
Before (Surface Knowledge):
JavaScript
function getUsers() {
let users = [];
fetch(url)
.then(response => response.json())
.then(data => {
users = data;
});
return users; // Always empty!
}I didn’t understand async. Just copied the pattern.
After (Actual Understanding):
JavaScript
async function getUsers() {
try {
const response = await fetch(url);
if(!response.ok) {
throw new Error('Failed to fetch');
}
const users = await response.json();
return users;
} catch(error) {
console.error('Error fetching users:', error);
return [];
}
}
// Usage:
getUsers().then(users => {
displayUsers(users);
});Now I understand:
- Why do we need
await? - Why is the function sync
- Why do we need error handling?
- How to actually return the data
The Interview I Passed (After Learning Fundamentals)
6 months later, same type of interview:
Interviewer: “Explain the difference between == and ===."
Me: “== compares values with type coercion. === compares values AND types. Example: '5' == 5 is true because == converts string to number. But '5' === 5 is false because they're different types. Always use === to avoid unexpected bugs."
Interviewer: “Good. What’s a closure?”
Me: “A closure is when a function remembers variables from its outer scope even after that outer function has finished executing. A common use case is creating private variables or factory functions.”
Interviewer: “Perfect. Can you explain the event loop?”
Me: “JavaScript is single-threaded. The event loop handles async operations by using a callback queue. When an async operation completes, its callback goes in the queue. After the current code finishes, the event loop checks the queue and executes callbacks. That’s why fetch doesn't block the code."
I got the job.
Same questions I failed before. But now I actually understand them.
What I Should’ve Done From Day 1
Week 1–2: Learn Fundamentals FIRST
- Variables (var/let/const, scope, hoisting)
- Data types (primitives vs objects)
- Functions (regular vs arrow, parameters, return)
- Conditionals and loops
Week 3–4: Build Simple Projects
- Calculator
- Todo list
- Simple games
Week 5–6: Advanced Fundamentals
- Objects and arrays (methods, reference types)
- DOM manipulation
- Events
Week 7–8: Async JavaScript
- Callbacks
- Promises
- Async/await
- Fetch API
Week 9–12: Build Real Projects
- Now you understand what you’re doing.
- Not just copying tutorials.
- Can debug problems
- Can you explain your code
I did it backwards. Built first, understood later.
Starting with fundamentals would’ve saved me 4 months of confusion.
Signs You Learned JavaScript Wrong
You might have the same problem if:
You can build projects, but can’t explain how they work
You Google the same questions repeatedly
Interview questions stump you even though you’ve coded for months
You copy code without understanding why it works
Debugging is just trying random fixes
You can’t read others’ code
You avoid certain concepts because they’re “confusing.”
If 3+ of these apply to you, go back to fundamentals.
How to Fill the Gaps
Option 1: Take a Break from Building (2 weeks)
Stop making projects. Focus purely on understanding.
Resources:
- JavaScript.info (work through core sections)
- “You Don’t Know JS” books.
- FreeCodeCamp JavaScript course
Option 2: Rebuild Old Projects (With Understanding)
Take a project you built.
For each line of code, ask:
- Why does this work?
- What would happen if I changed it?
- Is there a better way?
Rewrite it with a better understanding.
Option 3: Teach What You Learn
Best way to test understanding: try to explain it.
Write blog posts. Make Twitter threads. Explain concepts to friends.
If you can’t explain it simply, you don’t understand it.
The Reality Check
Learning fundamentals is boring.
Building projects is fun.
But if you skip fundamentals, you’ll hit a wall.
You’ll build 10 todo apps and still not understand closures.
You’ll copy-paste fetch requests and still not understand promises.
You’ll pass beginner tutorials and fail intermediate interviews.
Projects prove you can follow instructions.
Fundamentals prove you can actually code.
What I Tell Beginners Now
“Spend your first month being bored.”
Learn the boring stuff:
- How variables work
- How functions work
- How objects work
- How async works
THEN build projects.
Those projects will be better. Your code will make sense. And you’ll actually know what you’re doing.
One month of boredom saves six months of confusion.
Your Challenge
Pick ONE fundamental concept you don’t fully understand:
- Closures
thiskeyword- Promises
- Event loop
- Prototypes
- Hoisting
Spend 2 hours learning just that one thing.
Read MDN docs. Watch explanations. Code examples. Test yourself.
Then explain it to someone (or write it down).
If you can’t explain it clearly, you don’t get it yet. Keep learning.
One concept at a time. That’s how you fill the gaps.
Building projects makes you feel like a developer.
Understanding fundamentals makes you actually be one.










