π JavaScript Fundamentals (Week-03): Understanding the Concepts That Every Developer Should Know
"Writing JavaScript code is one thing, but understanding what happens behind the scenes is what makes you a better developer."
When I first started learning JavaScript, I knew how to declare variables and write functions. However, I often found myself asking questions like:
- Why are there three ways to declare variables?
- What exactly is hoisting?
- How does JavaScript execute my code?
- Why can an inner function access variables from its parent function?
- What does the
thiskeyword actually refer to? - Why do developers keep talking about writing clean code?
This week, I focused on understanding these core JavaScript concepts instead of simply memorizing syntax.
In this article, I'll explain each concept in a beginner-friendly way with examples and practical explanations.
π Topics Covered
- Variables (
var,let,const) - Hoisting
- Lexical Scope
- Execution Context
- Call Stack
- Closures
-
thisBinding - DRY Principle
- KISS Principle
Let's start from the beginning.
π¦ Variables in JavaScript
What is a Variable?
A variable is a named container used to store data in memory.
Instead of writing the same value repeatedly, we store it inside a variable and reuse it whenever required.
For example,
let name = "Sai";
console.log(name);
Output
Sai
Here,
-
letβ Variable declaration keyword -
nameβ Variable name -
"Sai"β Stored value
Why Do We Need Variables?
Imagine writing this:
console.log("Sai");
console.log("Sai");
console.log("Sai");
If the value changes, every occurrence must be updated.
Using variables,
let name = "Sai";
console.log(name);
console.log(name);
console.log(name);
Now changing one line updates every usage.
Variables improve:
- Readability
- Reusability
- Maintainability
Types of Variables
JavaScript provides three ways to declare variables.
- var
- let
- const
Although all three create variables, they behave differently.
var
var was introduced in the first version of JavaScript.
Characteristics:
- Function Scoped
- Can be redeclared
- Can be reassigned
- Hoisted
Example
var city = "Hyderabad";
var city = "Bangalore";
console.log(city);
Output
Bangalore
Because var allows redeclaration, it can accidentally overwrite existing values.
let
let was introduced in ES6.
Characteristics
- Block Scoped
- Cannot be redeclared
- Can be reassigned
Example
let age = 20;
age = 21;
console.log(age);
Output
21
const
const is used when the variable should not be reassigned.
Characteristics
- Block Scoped
- Cannot be redeclared
- Cannot be reassigned
const PI = 3.14;
console.log(PI);
Output
3.14
π Hoisting
What is Hoisting?
Hoisting is JavaScript's behavior of processing declarations before executing the code.
This doesn't mean the code physically moves. Instead, during the memory creation phase, JavaScript prepares variables and functions before execution begins.
Example
console.log(language);
var language = "JavaScript";
Output
undefined
Internally JavaScript treats it like this:
var language;
console.log(language);
language = "JavaScript";
Variables declared using let and const are also hoisted, but they remain inside the Temporal Dead Zone (TDZ) until they are initialized.
π Lexical Scope
Scope determines where a variable can be accessed.
Lexical Scope means an inner function can access variables declared in its outer function because of where it is written.
Example
function outer(){
let message="Hello";
function inner(){
console.log(message);
}
inner();
}
outer();
Output
Hello
JavaScript first searches inside inner(). If the variable isn't found, it looks in the outer function.
This searching process is called the Scope Chain.
βοΈ Execution Context
Before JavaScript executes any code, it creates an Execution Context.
Think of it as JavaScript's workspace.
It stores:
- Variables
- Functions
- Scope
- Value of
this
Every Execution Context has two phases.
Memory Creation Phase
During this phase,
- Variables are allocated memory.
-
varvariables are initialized withundefined. - Function declarations are stored.
Execution Phase
During this phase,
- Variables receive values.
- Statements execute one by one.
- Function calls create new execution contexts.
π Call Stack
The Call Stack keeps track of function execution.
It follows the Last In, First Out (LIFO) principle.
Example
function first(){
second();
}
function second(){
console.log("Inside Second");
}
first();
Output
Inside Second
Execution Flow
first()
β
second()
β
console.log()
β
Return
β
Return
π Closures
One of JavaScript's most powerful features is the Closure.
A Closure is created when an inner function remembers variables from its outer function even after the outer function has finished execution.
Example
function counter(){
let count = 0;
return function(){
count++;
console.log(count);
}
}
const increment = counter();
increment();
increment();
increment();
Output
1
2
3
Closures are commonly used for:
- Counters
- Data Privacy
- Event Handlers
- Module Pattern
π― Understanding this
The this keyword refers to the object that calls a function.
Its value depends on how the function is called.
Implicit Binding
const user = {
name:"Sai",
greet(){
console.log(this.name);
}
}
user.greet();
Output
Sai
Explicit Binding
JavaScript provides call(), apply(), and bind() to explicitly decide what this should refer to.
function greet(){
console.log(this.name);
}
const user={
name:"Sai"
};
greet.call(user);
Output
Sai
new Binding
function Student(name){
this.name=name;
}
const s1=new Student("Sai");
console.log(s1.name);
Output
Sai
Arrow Function
Arrow functions don't have their own this.
Instead, they inherit it from their surrounding scope.
β¨ DRY Principle
DRY stands for
Don't Repeat Yourself
Instead of repeating code,
console.log("Welcome Sai");
console.log("Welcome Rahul");
write
function greet(name){
console.log(`Welcome ${name}`);
}
greet("Sai");
greet("Rahul");
Benefits
- Reusable code
- Easy maintenance
- Less duplication
π‘ KISS Principle
KISS stands for
Keep It Simple, Stupid
Simple code is easier to understand than complicated code.
Instead of
function isEven(num){
if(num%2===0){
return true;
}
else{
return false;
}
}
write
function isEven(num){
return num%2===0;
}
Simple code is:
- Easy to debug
- Easy to maintain
- Easy to understand
π― Key Takeaways
After completing this week's learning, I understood that:
- Variables store data.
- Hoisting happens during memory creation.
- Lexical Scope decides where variables are accessible.
- Execution Context provides the environment for code execution.
- The Call Stack manages function execution.
- Closures preserve variables after a function finishes.
- The value of
thisdepends on how a function is called. - Following DRY and KISS helps write clean and maintainable code.
π Conclusion
Learning JavaScript isn't just about writing codeβit's about understanding how the language works behind the scenes. Concepts like execution context, scope, closures, and this binding explain why our code behaves the way it does. By combining these fundamentals with clean coding principles like DRY and KISS, we can write code that is not only correct but also easy to read, maintain, and extend.
Top comments (1)
I remember the exact moment I thought I understood JavaScript. I was writing a simple counter, closures made sense, this was predictable, and I felt invincible. Then I passed a method as a callback and watched this betray me like a friend who just learned the word "actually." That is when I realized JavaScript is not a languageβit is a puzzle box that occasionally compiles.
This is a really solid primer for anyone coming from other languages or just starting out. The progression from variables to closures is logical, and I appreciate that you included not just the "what" but the "why"βespecially the DRY and KISS sections. A lot of tutorials skip the design principles, but those are the things that separate "code that works" from "code that does not make the next person cry."
One thing I would add for readers who are still wrapping their heads around closures: the mental model that finally clicked for me was thinking of a closure as a backpack that a function carries around. When the inner function leaves its parent, it takes a backpack filled with the variables it needs. That way, even when the parent function is long gone, the backpack is still there. It is not a perfect analogy, but it helped me stop thinking of closures as magic.
Also, since you covered execution context and the call stack so clearly, I would love to see a follow-up that walks through the event loop and microtask queue. That is where the real "how does this actually run" questions live, and your style would make it accessible without dumbing it down.
Anyway, great work on this. Your future self will thank you for writing it, and so will everyone who reads it.