JavaScript: Variables & Data Types

Understand how to declare and scope variables, how hoisting works, and every data type JavaScript provides.

Variable Declarations: var, let, const

JavaScript has three ways to declare variables. const is the default choice, let is for values that need reassignment, and var is legacy and should be avoided in modern code.

var / let / const

Each keyword has different scoping rules and reassignment behaviour.

  • const: block-scoped, cannot be reassigned; use by default
  • let: block-scoped, can be reassigned; use when value changes
  • var: function-scoped, hoisted, can be re-declared; avoid in modern JS
  • const on an object means the binding is fixed, not the object's contents

const, let, and var

const fixes the binding, not the value inside an object.

Featurevarletconst
ScopeFunctionBlockBlock
HoistedYes (undefined)Yes (TDZ)Yes (TDZ)
Re-declarableYesNoNo
ReassignableYesYesNo
Use today?❌ Avoid✅ When needed✅ Default

Variable Scope

Scope determines where a variable is accessible. JavaScript has three scopes: global (entire program), function (inside a function), and block (inside any {}).

The Three Scopes

Scope controls visibility and lifetime of a variable.

  • Global scope, declared outside any function or block; accessible everywhere
  • Function scope, var declared inside a function; invisible outside it
  • Block scope, let/const inside {}; invisible outside the block
  • Inner scopes can read outer variables, but not vice versa

Global, Function, and Block Scope

Each scope level can see its parent, but parents cannot see inward.

Hoisting Behaviour

Hoisting is JavaScript's behaviour of moving declarations to the top of their scope before execution. Only the declaration is hoisted, the assignment stays where it is.

Hoisting

Declarations are processed before any code runs, assignments are not.

  • var: declaration hoisted, initialised to undefined until the assignment line
  • function declarations: fully hoisted, callable before the definition
  • let / const: hoisted but not initialised; accessing them before declaration throws a ReferenceError (TDZ)
  • Relying on hoisting is a code smell, always declare before use

Hoisting in Action

var gives undefined before its line; function declarations are fully available.

Temporal Dead Zone (TDZ)

The Temporal Dead Zone is the region from the start of a block up to the point where a let or const is declared. Accessing the variable inside the TDZ throws a ReferenceError.

Temporal Dead Zone

let and const are hoisted but not initialised, the gap is the TDZ.

  • The TDZ starts at the top of the block, not the top of the file
  • Accessing a let/const variable inside the TDZ always throws a ReferenceError
  • The TDZ ends exactly at the declaration line
  • TDZ exists to catch bugs, it is intentional, unlike var's silent undefined

Temporal Dead Zone Demo

Accessing city before its declaration line throws, even though it is in the same block.

Primitive Data Types: Overview

Primitives are immutable values stored directly in the variable. JavaScript has seven primitive types. When you assign a primitive to another variable, a copy of the value is made, they are completely independent.

The 7 Primitive Types

Primitives are copied by value, assigning one creates an independent copy.

  • string: text in single, double, or backtick quotes
  • number: integers, floats, NaN, and Infinity
  • boolean: true or false
  • null: intentional absence of a value
  • undefined: declared but not yet assigned
  • symbol (ES6): guaranteed-unique identifier
  • bigint (ES2020): integers beyond Number.MAX_SAFE_INTEGER

String

A string is a sequence of characters used to represent text. Strings are immutable, you cannot change a character in place, but you can produce a new string from an existing one.

string

Text data, wrapped in single quotes, double quotes, or backticks.

  • Single '..' and double "..." quotes are equivalent
  • Backtick `...` strings (template literals) support interpolation with ${"{"}{expression}{"}"}
  • Strings are zero-indexed: "hello"[0] is "h"
  • Key methods: .length, .toUpperCase(), .includes(), .slice(), .trim()

String Basics

Template literals replace concatenation, cleaner and less error-prone.

Number

JavaScript has a single number type for both integers and floating-point values. It also includes the special values NaN and Infinity.

number

All numeric values, integers and floats, share the same type.

  • Integers and floats: 42, 3.14, -7
  • NaN: result of invalid arithmetic ("abc" * 2); typeof NaN === "number"
  • Infinity: result of dividing by zero: 1 / 0
  • Number.MAX_SAFE_INTEGER is 2^53 - 1, beyond this, use BigInt
  • Floating-point precision: 0.1 + 0.2 !== 0.3, use .toFixed() for display

Number Quirks

NaN, Infinity, and floating-point imprecision are the three number surprises to know.

Boolean

A boolean has exactly two values: true or false. Booleans are the result of comparisons and drive all conditional logic. Every value in JavaScript is either truthy or falsy.

boolean

true or false, plus the concept of truthy and falsy for all other values.

  • Falsy values: false, 0, "", null, undefined, NaN
  • Everything else is truthy, including "0", [], and {}
  • Use Boolean(value) or !! to explicitly convert to boolean
  • Comparison operators return booleans: ===, !==, >, <

Booleans and Truthiness

Careful: "0", [], and {} are all truthy even though they look "empty".

Null

null is an intentional assignment that means "no value". It is used when you explicitly want a variable to hold nothing, as opposed to simply not having been assigned yet.

null

An explicit empty value, you set it on purpose to signal 'nothing here'.

  • typeof null === "object": a historic JavaScript bug; check with === null
  • Use null to intentionally clear a variable
  • Different from undefined: undefined means "never assigned"; null means "deliberately empty"
  • Nullish coalescing ?? treats both null and undefined as "no value"

null in Practice

Always check for null with === null, never with typeof.

Undefined

undefined means a variable has been declared but no value has been assigned to it yet. JavaScript itself sets values to undefined automatically in several situations.

undefined

The default value JavaScript assigns when something has not been initialised.

  • Declared variable with no assignment: let x;x is undefined
  • Missing function argument: calling fn() when fn expects a param
  • Accessing a non-existent object property: obj.missing
  • Functions with no return statement return undefined

Where undefined Appears

undefined is set by JavaScript, null is set by you. Both signal 'no value' but for different reasons.

Symbol (ES6)

A Symbol is a guaranteed-unique, immutable primitive introduced in ES6. Every call to Symbol() creates a value that is never equal to anything else, including another Symbol with the same description.

symbol

A unique identifier, primarily used as non-colliding object property keys.

  • Every Symbol() call produces a unique value, even with the same description string
  • The description is just a label for debugging, not the value
  • Used as object keys to avoid property name collisions in libraries
  • Symbols are not enumerated in for...in or Object.keys()

Symbol Uniqueness

Same description string, but id1 and id2 are never equal, uniqueness is guaranteed.

BigInt (ES2020)

BigInt represents integers of arbitrary size, solving the precision problem that occurs when numbers exceed Number.MAX_SAFE_INTEGER (253 − 1).

bigint

Arbitrarily large integers, append n to a literal or use BigInt().

  • Created with an n suffix: 9007199254740993n
  • Cannot be mixed with regular number in arithmetic, must convert explicitly
  • Use for cryptography, financial calculations, or IDs that exceed safe integer range
  • typeof 1n === "bigint"

BigInt vs Number Precision

Regular numbers silently lose precision beyond MAX_SAFE_INTEGER, BigInt stays exact.

Reference Data Types: Overview

Objects, arrays, and functions are reference types. Variables do not hold the value directly, they hold a reference (memory address) to it. Assigning a reference type to another variable gives both variables the same pointer.

Reference Types

Two variables can point to the same object, mutating through one affects the other.

  • Object: unordered key/value pairs
  • Array: ordered, indexed list (a specialised object)
  • Function: callable object; first-class value in JavaScript
  • Clone with spread {...obj} or [...arr] to avoid shared-reference bugs

Objects

An object is a collection of key/value pairs. Keys are strings (or Symbols) and values can be any type, including other objects or functions.

Object

The fundamental building block for structured data in JavaScript.

  • Defined with curly braces: { key: value }
  • Access with dot notation obj.key or bracket notation obj["key"]
  • Methods are functions stored as properties
  • Object.keys(), Object.values(), Object.entries() iterate over properties

Object Basics

Methods are just functions stored as object properties.

Arrays

An array is an ordered, zero-indexed list. It is a specialised object where keys are numeric indices. Arrays can hold mixed types and have a rich set of built-in methods.

Array

An ordered list, the most common data structure in JavaScript.

  • Defined with square brackets: [1, "two", true]
  • Zero-indexed: first element is at index 0
  • Key methods: .push(), .pop(), .map(), .filter(), .find()
  • Use Array.isArray(arr) to check, typeof [] returns "object"

Array Basics

push adds to the end, map transforms every element into a new array.

Functions as Reference Types

Functions in JavaScript are first-class objects, they can be stored in variables, passed as arguments, and returned from other functions. typeof fn returns "function", but functions are still reference types internally.

Function as a Value

Functions are objects, they can be assigned, passed, and returned like any value.

  • A function stored in a variable is a function expression
  • Passing a function as an argument makes it a callback
  • Returning a function from a function is the basis of closures
  • typeof fn === "function", special typeof result for callable objects

Functions as First-Class Values

double is passed directly into map as a callback, no wrapper needed.

Type Checking with typeof

The typeof operator returns a string describing the type of a value. It is the primary runtime type-checking tool, with two well-known quirks.

typeof Operator

Returns a string label for the operand's type, useful for runtime guards.

  • typeof "hi""string"
  • typeof 42"number"
  • typeof true"boolean"
  • typeof undefined"undefined"
  • typeof null"object", historic bug; check with === null
  • typeof []"object", use Array.isArray() for arrays

typeof: Full Reference

null and arrays both return 'object', use dedicated checks for those two.

Type Coercion and Conversion

Coercion is implicit type conversion done automatically by JavaScript. Conversion is explicit, you call a function to change the type deliberately. Coercion is a common source of surprising bugs.

Coercion vs Conversion

Implicit coercion happens silently, always prefer explicit conversion in production code.

  • Implicit: "5" + 3"53" (number coerced to string by +)
  • Implicit: "5" - 32 (string coerced to number by -)
  • Explicit: Number("5"), String(42), Boolean(0)
  • Use === (strict equality) to skip coercion in comparisons

Coercion vs Explicit Conversion

The + operator is the trickiest, it concatenates if either side is a string.

Type Checking with typeof

The typeof operator returns a string describing the type of a value. It is the primary tool for runtime type checking, with one well-known quirk.

typeof Operator

Returns a string label for the operand's type, useful for runtime guards.

  • typeof "hi""string"
  • typeof 42"number"
  • typeof true"boolean"
  • typeof undefined"undefined"
  • typeof null"object", historic bug, check with === null instead
  • typeof []"object", use Array.isArray() to detect arrays

typeof in Practice

Always use Array.isArray() for arrays and === null for null, typeof alone is not enough.

Type Coercion and Conversion

Coercion is implicit type conversion done automatically by JavaScript. Conversion is explicit, you call a function to change the type deliberately. Coercion is a common source of surprising bugs.

Coercion vs Conversion

Implicit coercion happens silently, always prefer explicit conversion in production code.

  • Implicit (coercion): "5" + 3"53" (number coerced to string)
  • Implicit (coercion): "5" - 32 (string coerced to number for -)
  • Explicit conversion: Number("5"), String(42), Boolean(0)
  • Use === (strict equality) to avoid coercion in comparisons

Coercion vs Explicit Conversion

Run this to see how + behaves differently from - when a string is involved.

Knowledge Check

1. Which declaration should you use by default in modern JavaScript?

2. What is the scope of a variable declared with let inside a block {}?

3. What does hoisting do to a var declaration?

4. What is the Temporal Dead Zone?

5. What does typeof null return?

6. Which of the following is a reference data type?

7. What is the result of "5" + 3 in JavaScript?

8. Which data type was introduced in ES2020 to handle integers beyond Number.MAX_SAFE_INTEGER?

9. What is the result of Boolean([]) in JavaScript?

10. Two variables point to the same object. You mutate the object through one variable. What happens to the other?

11. What makes every Symbol() call unique?