Difficulty: Beginner
Variables are the foundation of any programming language - they are named containers that store data values. In JavaScript, you declare variables using one of three keywords: var, let, or const. Understanding the differences between these three is critical for writing correct and maintainable code.
The var keyword is the oldest way to declare variables in JavaScript. It has function scope (not block scope), meaning a var declared inside an if block or for loop is still accessible outside that block. It also gets hoisted to the top of its function, meaning you can reference it before its declaration line (though its value will be undefined). Because of these quirky behaviors, var is considered legacy and should be avoided in modern code.
The let keyword, introduced in ES6 (2015), declares a block-scoped variable. A let variable declared inside a curly brace block {} is only accessible within that block. Unlike var, accessing a let variable before its declaration throws a ReferenceError (the Temporal Dead Zone). Use let when you need a variable whose value will change over time, such as counters or accumulators.
The const keyword also declares a block-scoped variable, but with one additional constraint: the binding cannot be reassigned after initialization. This does not mean the value is immutable - if you declare a const object or array, you can still modify its properties or elements. You just cannot reassign the variable to point to a different value. Use const by default for all variables, and only switch to let when you know the value needs to change.
JavaScript has seven primitive data types: string (text enclosed in quotes), number (integers and floating-point values, including Infinity and NaN), boolean (true or false), null (intentional absence of value), undefined (variable declared but not assigned), symbol (unique identifier, added in ES6), and bigint (arbitrary-precision integers, added in ES2020). Everything that is not a primitive is an object, including arrays, functions, dates, and regular expressions.
The typeof operator is the primary way to check a value's type at runtime. It returns a string like "string", "number", "boolean", "undefined", "object", "function", "symbol", or "bigint". Be aware of a well-known quirk: typeof null returns "object" instead of "null" - this is a bug from the original JavaScript implementation that can never be fixed because too much existing code depends on it.
// var is function-scoped and hoisted
function varExample() {
console.log(x); // undefined (hoisted)
var x = 10;
if (true) {
var x = 20; // Same variable!
}
console.log(x); // 20
}
varExample();
// let is block-scoped
function letExample() {
let y = 10;
if (true) {
let y = 20; // Different variable
console.log(y); // 20
}
console.log(y); // 10
}
letExample();
// const cannot be reassigned
const PI = 3.14159;
// PI = 3; // TypeError: Assignment to constant variable
console.log(PI);
var leaks out of blocks and gets hoisted with an undefined value. let and const respect block boundaries and throw errors if accessed before declaration. const prevents reassignment but does not make values immutable.
const str = "Hello World"; // string
const num = 42; // number
const float = 3.14; // number (no separate float type)
const bool = true; // boolean
const empty = null; // null
let notAssigned; // undefined
const sym = Symbol("id"); // symbol
const big = 9007199254740993n; // bigint
console.log(typeof str); // "string"
console.log(typeof num); // "number"
console.log(typeof float); // "number"
console.log(typeof bool); // "boolean"
console.log(typeof empty); // "object" (famous bug!)
console.log(typeof notAssigned); // "undefined"
console.log(typeof sym); // "symbol"
console.log(typeof big); // "bigint"
JavaScript has 7 primitive types. Note that typeof null returns 'object' - this is a historical bug. There is no separate integer or float type; all numbers are IEEE 754 double-precision floating point (64-bit).
// const prevents reassignment, NOT mutation
const user = { name: "Alice", age: 25 };
user.age = 26; // This is allowed
user.email = "a@b.c"; // Adding properties is allowed
console.log(user);
// user = {}; // TypeError: Assignment to constant variable
const colors = ["red", "green"];
colors.push("blue"); // Modifying array contents is allowed
console.log(colors);
// colors = []; // TypeError: Assignment to constant variable
A common misconception is that const makes values immutable. It only prevents the variable from being reassigned to a new value. Object properties and array elements can still be changed. Use Object.freeze() if you need true immutability.
// typeof works for most cases
console.log(typeof "hello"); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof Symbol()); // "symbol"
console.log(typeof function(){}); // "function"
// But typeof has limitations
console.log(typeof null); // "object" (bug)
console.log(typeof [1, 2, 3]); // "object" (arrays are objects)
console.log(typeof { a: 1 }); // "object"
// Better checks for specific types
console.log(Array.isArray([1, 2, 3])); // true
console.log(null === null); // true
console.log(Number.isNaN(NaN)); // true
console.log(Number.isFinite(42)); // true
typeof is useful but has blind spots: null returns 'object' and arrays return 'object'. Use Array.isArray() for arrays, strict equality with null for null checks, and Number.isNaN() for NaN checks.
var, let, const, Primitive Types, typeof Operator, Type Checking