Understanding Variables and Data Types in JavaScript

Understanding Variables and Data Types in JavaScript

posted Originally published at codexninja.hashnode.dev 12 min read

I remember when i first started learning JavaScript everything was so confusing, i felt completely overwhelmed.
Variables, Data Types, let, const, Scope, Hoisting, Temporal Dead Zone (TDZ)... ahh so many things.

It felt like everyone else understand it and i was just trying to memorize these things without actually understanding what was going on under the hood.

So in this blog i’m going to cover and explain slowly, clearly & practically everything the way i wish someone had explained it to me when i started learning JavaScript.

By the end of this blog you won’t just know the definitions but you will understand how JavaScript behaves actually.

So let’s start from the very beginning.


What are JavaScript Variables?

Let’s forget the technical definition for now and think of a variable like a box
So start with this Imagine you have a box

  • You put something inside it

  • Write a lable on it

  • Open it later to see what’s inside the box

  • Later replace what’s inside if needed

That box is exactly what a variable is

In JavaScript a variable is simply a named container that stores data so we can use it later.

for example:

let name = "Nausheen";

How I see it in my head:

  • name => the label on the box

  • "Nausheen" => what's inside the box

But the question is: Why do we need variables?
The reason is simple because programs need memory

When you log into a website, it needs to remember:

  • Your username

  • Your password

  • Whether you’re logged in or not

  • What’s in your cart

Without variables, JavaScript wouldn’t be able to remember anything not even for a second.

Variables are the foundation of everthing


How I Declare Variables (var, let, const)

Here we go In JavaScript, we have three ways to create variables:

  • var

  • let

  • const

But the thing is they are not the same. And this is where beginners usually get confused.
So, let me break it down the way I personally understand it and where to use them.


1. var (old way)

var age = 20;

yup simple, this is how it works.

And yes, you can also change it:

age = 21;

But here’s what I’ve learned over time:
var has some strange behaviors that can create confusion in larger programs.

For example:

var city = "Delhi";
var city = "Mumbai"; // No error

It allows redeclaration in the same scope.

That might not seem dangerous now, but in a big project, this can silently overwrite values & cause bugs that are hard to track.

That’s why in morder JavaScript, we mostly avoid var

Not because it’s bad, but because there are safer alternatives.


2. let (Flexible and Modern)

let score = 50;

I use let when I know that value might change later.

score = 60; // Perfectly fine

But here’s something important:

let score = 70; // Error

You cannot redeclare a let variable in the same scope.

And honestly, I like this.
Because it prevents accidental mistakes.


3. const (Fixed and Safe)

const birthYear = 2004;

Ok so, when I write const I'm basically saying:

I don’t want this value to change

And JavaScript respects that.

birthYear = 2005; // Error

This is why I personally follow this rule:

  • I use const by default

  • I switch to let only when I need to change the value

This makes code safer and more predictable.


Difference Between var, let, and const

![](https://miro.medium.com/v2/resize:fit:875/0*KDwJv8_NmwbWIptj.png align="center")

If you’re just starting out, here’s what I’d suggest you:

Use const most of the time
Use let when necessary
Forget var for now


What Are Data Types?

What’s Inside the Box?

Now let’s talk about what we actually store inside variables.

When I say "data type" I'm simply asking:
What kind of value is inside this variable?

If variables are boxes, data type describes what kind of thing is inside the box.

In JavaScript, there are many types. But as beginners, we focus on primitive data types.

Basically, there are two categories of data types:

  1. Primitive Data Types

  2. Non-Primitive Data Types

But in this blog, we will only focus on primitive data types.

The main primitive types in JavaScript are:

  • String

  • Number

  • Boolean

  • Null

  • Undefined

Let’s understand them properly


1. String - Text Data

Whenever I want to store some text data, I use a string.

let name = "Nausheen";
let message = "Hello World";

String are always written inside quotes.
Without quotes, JavaScript thinks it’s a variable name.

Strings are used for:

  • Name

  • Emails

  • Messages

  • Addresses

Basically, anything that’s text.


2. Number - Numeric Values

let age = 21;
let price = 99.99;

In JavaScript, there is only one number type.

It doesn’t seperate integers and decimals unlike many other programming languages

Whether it’s 21 or 99.99 , both are simply number.

Numbers are used for:

  • Age

  • Score

  • Prices

  • Calculations


3. Boolean - True or False

Booleans are very simple but very powerful.

They only have two values:

  • true

  • false

let isLoggedIn = true;
let hasPermission = false;

Booleans are mainly used in decisions.

For example:

  • Is the user logged in?

  • Did the form submit successfully?

  • Is the password correct?

Most conditions in programming rely on booleans.


4. null - Intentionally Empty

When I use null, I'm basically saying:

I’m intentionally keeping this empty for now.

let selectedUser = null;

This means:
I will assign a real value later.


5. undefined - Not Assigned Yet

If i declare a variable but don’t give it a value:

let score;

JavaScript automatically assigns it undefined.

That means:
The variable exist, but nothing is inside it yet.

The difference between null and undefined is subtle but important:

  • undefined => JavaScript gives it automatically

  • null => You assign it intentionally


What is Scope?

When I first heard the word scope, it sounded technical and abstract. But one I simplified it in my mind everything become clearer.

So, Scope simply answers one question:
Where can I access this variable?

That’s it……

Scope determines the visibility & accessibility of variables in different parts of your prograrm.

Let’s take an example of “The Rooms in a House”
Imagine scope as rooms inside a house

Each { } block is like a separate room.

If I place something inside one room, I can only access it inside that room. I cannot randomly walk into another room & expect that thing to be there.

In JavaScript, a block looks like this:

{
    let message = "Hello there";
    console.log(message); // It works
}
console.log(message); // ReferenceError

Here, message was created inside the block (inside the "room").

So when we try to access it outside the block, JavaScript throws a ReferenceError.

Not because the value is undefined
But because it does not exist in that scope.

That’s an important distinction.


Block Scope (let & const)

Variables declared using let and const follow block scope.

A block is anything inside { }:

  • if statements

  • for loop

  • while loop

  • plain curly braces

  • function

if (true) {
    let age = 22;
}
console.log(age); // ReferenceError

Here, age only exists inside that if block.

Once execution leaves that block, the variable is gone.

This behavior makes your code safer because:

  • It prevents accidental overwriting of variables

  • It avoids naming conflicts

  • It keeps variables limited to where they are needed

This is one of the main reason why JavaScript prefers let and const.

They give you controlled scope.


Function Scope

Before ES6, Javascript only had function scope.

That means variables declared with var are accessible anywhere inside the function they were declared in regardless of blocks.

function test() {
    if (true) {
        var name = "Nausheen";
    }
    
    console.log(name); // works
}

Even though name was declared inside the if block, it is accessible outside that block but still inside the function.

Why?
Because var doesn't follow block scope.

This difference is the root cause of many bugs in older JavaScript code.


Why var Is Problematic?

Let’s look at this code example:

if (true) {
    var count = 10;
}
console.log(count); // Works (unexpected behavior for beginners)

Here, count leaks outside the block.

This can cause:

  • Variable collisions

  • Hard to debug errors

  • Accidental overwrites

  • Confusing behavior in loops

That’s why modern JavaScript discourages to use var unless absolutely necessary.


Global Scope

If we declare a variable outside of any block or function, it belongs to the global scope.

Let’s see a Example:

let globalMessage = "Hi"; // Global variable
function greet() {
    console.log(globalMessage); // Accessible
}

Global variables can be accessed everywhere in the program.

But here’s the danger:

Too many global variables make your code messy & hard to maintain.
Think of global scope as the entire house, if you throw everything in the living room, it becomes chaos.

Good developers minimize global scope usage.


Function Inside Function

Until now, we talked about scope like rooms in a house.

Let’s now imagine something more interesting
what if a room had another room inside it?

Now let’s see what happens when functions are inside other functions…

Yes this is completely valid look at this code:

function outer() {
    let outerMessage = "Hello from outer";
    function inner() {
        console.log("Hello from inner");
    }
    
    inner();
}
outer();

Here, outer is one room, inner is smaller room inside it.

Now let’s test something important.

function outer() {
    let outerMessage = "Hello from outer";
    function inner() {
        console.log(outerMessage); // works
    }
    
    inner();
}
outer();

The inner function can access the outer function’s variable.

But, can outer access inner’s variable to, let test that also.

function outer() {
     function inner() {
        let secret = "Hidden"
    }
    
    inner();
    console.log(secret); // Error
}
outer();

Oops outer cannot access inner’s variables.

So, remember access only flow upward means:
Child -> Parent
Never Parent -> Child

This idea is the foundation of lexical scope.

But before we go there……
First we need to understand something deeper.

How JavaScript actually runs you code


How JavaScript Actually Runs Your Code (Execution context)

Most beginners think JavaScript reads code line by line.

Reads line 1
Then line 2
Then line 3…

But that’s not exactly true.

JavaScript runs your code in two phases.
And this is where everything changes.


Phase 1: Memory Creation Phase

Before executing anything…

JavaScript first scans you entire code.

  • It allocates memory for variables

  • Stores function declarations

  • Sets up scope

But keep this in mind:

Variables don’t get their real values yet.

  • var => initialized with undefined

  • let and const => created but not initialized

  • Functions => fully stored in memory

console.log(name); // undefined
var name = "Nausheen";

undefined why?

Because during memory phase:

var name = undefined;

Then during execution phase:

name = "Nausheen";

This behavior is called hoisting, but we’ll go deeper into that soon… yes we’re going to cover that too.


Phase 2: Code Execution Phase

Now JavaScript starts running the code line by line.

Values are assigned
Functions get executed
console.log() run

Memory Phase always comes first
Then comes the Execution Phase

Simple as that. Yes nothing scary


Call Stack

Every time a function runs… JavaScript creates a new execution context.

These execution contexts are stacked on top of each other.
Think of the call stack like a stack of plates

When you call a function:

Javascripit puts that function on top of the stack

When the function finishes:

JavaScript removes it from the top

That’s it

It always works Last In, First Out (LIFO).
The last function added is the first one removed

function one() {
    two();
}
function two() {
    console.log("Hello");
}
one();

Let’s see what happens step by step:

  1. Global code starts running

  2. one() is called -> added to stack

  3. Inside one(), two() is called -> added on top

  4. two() finishes -> removed

  5. one() finishes -> removed

  6. Back to global

So, flow looks llike this:

Global
one()
two()

Then it goes backward:

two() removed
one() removed
back to Global

This is how JavaScript manages execution


Hoisting - What It Actually is (And What Most People get Wrong)

Now that you understand execution context… Hoisting will make sense.

When I first learned hoisting, someone told me:

JavaScript moves variables to the top

And honestly?..

That explanation confused me more than it helped.

Because JavaScript doesn’t physically move your code
Nothing jumps to the top
Your code stays exactly where you wrote it.

So the question is what’s really happening?

To understand hoisting properly, we have to go back to something we already learned:

Execution context

Memory Phase

Execution Phase

Hoisting is simply a result of how JavaScript prepares memory before running code.


How var Hoisting Actually Works

Let’s see:

console.log(name); // undefined
var name = "Nausheen";

Output is undefined why not error?

Because during memory phase
JavaScript does this internally:

var name = undefined;

Then during execution phase:

name = "Nausheen";

So when console.log runs,
name exists but its value is undefined

So, remember this is important:

var is declared during compilation
It is also initialized to
undefined during compilation

That’s why it’s accessible early.


Why var Can Be Dangerous

Now look at this:

function example() {
  console.log(x); // undefined
if (false) {
    var x = 10;
  }
  console.log(x); // undefined
}
example();
undefined
undefined

Even though the if block never ran.

Why?
Because var x was hoisted to the function scope.

This is the dangerous part.
var ignores block scope.

It can silently create undefined variables in unexpected places.

This is one of the biggest reasons modern JavaScript prefers let and const.


Function Hoisting

Function declarations behave differently it’s even more powerful.

They are not just declared
They are fully initialized during compilation

greet();
function greet() {
  console.log("Hello!");
}

This works perfectly.

Because during compilation:

greet: [complete function stored in memory]

The entire function body is available before execution begins.

That’s why function declarations are called:
Fully hoisted

Function Expression vs Function Declaration

Now this is important.

sayHi();
var sayHi = function () {
  console.log("Hi");
};

Now this throws:
TypeError: sayHi is not a function

Why?
Because only variable is hoisted

var sayHi = undefined;

Which causes the error.

The difference is

  • Function Declaration => Fully hoisted

  • Function Expression => Only variable hoisted


Now Let’s Talk About let and const

Here’s where things get misunderstood

Many people say:

let and const are not hoisted

That is wrong…

They are hoisted

But they behave differently

During compilation:

  • let and const are registered in memory

  • But they are NOT initialized

This creates something called… TDZ


Temporal Dead Zone (TDZ)

Another scary term TDZ. Ahh, now what’s that?
Don’t worry. I’ll keep it simple

This is one of the most misunderstood concepts.

The Temporal Dead Zone is:

The time between when a variable is hoisted

*And when it is initialized

Let’s see it:

console.log(age);
let age = 25;

This throws:
ReferenceError: Cannot access 'age' before initialization

Why?
Beause:

  • age exists in memory

  • But it is uninitialized

  • It is in the Temporal Dead Zone

The TDZ starts:
At the beginning of the block

And ends:
At the line where the variable is declard

TDZ Is About Time, Not Position

Very important, TDZ is not about where the variable is written.
It’s about time.

function test() {
  console.log(score);
  let score = 100;
}
test();

From the moment the function starts running,
score exists.

But until let score = 100 executes,
it is in the TDZ.

That’s why accessing it throws an error.

Proof That let Is Actually Hoisted

Look at this:

let x = "outer";
function demo() {
  console.log(x);
  let x = "inner";
}
demo();

This throws ReferenceError.

If let x inside demo was NOT hoisted,
JavaScript would use the outer x and print "outer".

But it doesn’t.

Why?
Because the inner x is hoisted to the function scope.
It creates a TDZ.

That proves:

let is hoisted
It is not initialized

That difference change everything.

How const Behaves

const works exactly like let regarding hoisting and TDZ.

But with one extra rule:
You must initialize it immediately.

const name; // SyntaxError

Correct:

const name = "Nausheen";

It is hoisted.
It has TDZ.
But it must be initialized.

Common misconceptions you should always remember

“Hoisting moves code to the top”
No.

Code stays exactly where you wrote it
Memory is prepared before execution

“Only var is hoisted”
Wrong.

All declarations are hoisted:

  • var

  • let

  • const

  • function

  • class

The difference is initialization timing.

“TDZ means variable doesn’t exist”
Wrong.

The variable exists.
It just cannot be accessed yet.

Why TDZ Is Actually a Good Thing

With var, this is allowed:

var total = total + 5;

This creates weird undefined behavior.

With let or const, you immediately get an error.

TDZ protects you from accidental bugs.
It forces cleaner code.
It makes JavaScript safer.

1 Comment

0 votes

More Posts

Understanding Variables and Data Types Fundamentals in JavaScript

Ritam137 - Mar 17

Optimizing the Clinical Interface: Data Management for Efficient Medical Outcomes

Huifer - Jan 26

Bridging the Silence: Why Objective Data Outperforms Subjective Health Reports in Elderly Care

Huifer - Jan 27

Breaking the AI Data Bottleneck: How Hammerspace's AI Data Platform Eliminates Migration Nightmares

Tom Smithverified - Mar 16

Understanding Basic Data Structures for Web Development

MasterCraft - Feb 16
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!