Sitemap
Saad ullah khan

Saad ullah khan

Exploring tech, coding, and the journey from beginner to freelancer. Sharing insights, lessons, and practical tips for learners.

I Learned JavaScript Wrong for 6 Months (Here’s What I Missed)

11 min readMar 10, 2026

--

Press enter or click to view image in full size

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 var leak outside loops, but let doesn't?
  • What’s “hoisting”?
  • Why can I use a name before declaring it with var but not let?
  • 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, and innerText?

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 changing user?name (works) vs user = {} (error)?
  • Why does array1 = array2 make 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 later

With 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() → this is user
  • greet() → this is window (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 Bob

Arrow 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:

  1. makeCounter() creates a variable count
  2. Returns a function that uses count
  3. Even though makeCounter() finished running, the returned function still remembers the count.
  4. Each time we call makeCounter(), it creates a new count

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)); // 15

Each 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:

  1. Code runs line by line.
  2. When it hits fetch, it says, "Start fetching, I'll check back later."
  3. Continues to next line (doesn’t wait)
  4. When fetch finishes, it puts the .then() function in a queue.
  5. 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); // 10

What’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
  • this keyword
  • 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
  • this keyword
  • 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.

--

--