2 min read
On this page

Variables, Types & Functions

Declaring Variables

JavaScript has three declaration keywords. Use two of them.

const name = "Alice";   // cannot be reassigned
let age = 30;           // can be reassigned
// var is legacy — never use it in new code

const is the default. Use let only when you need to reassign. var has function scope instead of block scope, hoists in confusing ways, and allows redeclaration.

const scores = [90, 85, 78];
scores.push(95);  // works — const prevents reassignment, not mutation

if (true) {
  const x = 10;
  let y = 20;
}
// x and y are not accessible here — block-scoped

Primitive Types

JavaScript has seven primitive types. Primitives are immutable and compared by value.

Type Example typeof
string "hello" "string"
number 42, 3.14, NaN "number"
boolean true, false "boolean"
null null "object" (historic bug)
undefined undefined "undefined"
symbol Symbol("id") "symbol"
bigint 9007199254740993n "bigint"

JavaScript has one number type: 64-bit IEEE 754 floating point. 0.1 + 0.2 produces 0.30000000000000004. For integers beyond Number.MAX_SAFE_INTEGER, use BigInt.

undefined means a variable was declared but not assigned. null means an intentional absence of value. Symbols create unique identifiers — two symbols are never equal, even with the same description.

Objects & Arrays

Everything that is not a primitive is an object. Objects are key-value pairs. Arrays are ordered lists (and are also objects).

const user = { name: "Alice", age: 30, roles: ["admin", "editor"] };
console.log(user.name);       // "Alice"
console.log(user["age"]);     // 30
console.log(user.roles[0]);   // "admin"

const colors = ["red", "green", "blue"];
colors.push("yellow");
console.log(colors.length);   // 4

Reference vs Value

Primitives are compared by value. Objects are compared by reference.

const a = { name: "Alice" };
const b = { name: "Alice" };
const c = a;
console.log(a === b);  // false — different objects
console.log(a === c);  // true — same reference

Functions

Declarations, Expressions & Arrows

// Declaration — hoisted, can be called before it appears
function greet(name) {
  return `Hello, ${name}!`;
}

// Expression — not hoisted
const greetExpr = function(name) {
  return `Hello, ${name}!`;
};

// Arrow — shorter, no own this
const add = (a, b) => a + b;
const square = x => x * x;
const getUser = () => ({ name: "Alice", age: 30 });  // object needs parens

Default Parameters

function createUser(name, role = "viewer") {
  return { name, role };
}
console.log(createUser("Alice"));          // { name: "Alice", role: "viewer" }
console.log(createUser("Bob", "admin"));   // { name: "Bob", role: "admin" }

Template Literals

Backtick strings support interpolation and multiline content.

const name = "Alice";
const age = 30;
const message = `${name} is ${age} years old.
She will be ${age + 1} next year.`;

Destructuring

Object Destructuring

const user = { name: "Alice", age: 30, role: "admin" };

const { name, age } = user;                          // basic
const { name: userName, role: userRole } = user;      // renaming
const { name: n, country = "Unknown" } = user;        // default values

Array Destructuring

const colors = ["red", "green", "blue"];
const [first, second] = colors;     // "red", "green"
const [, , third] = colors;         // skip to "blue"

let a = 1, b = 2;
[a, b] = [b, a];                    // swap

Destructuring in Function Parameters

function formatUser({ name, age, role = "viewer" }) {
  return `${name} (${age}) — ${role}`;
}

Spread & Rest Operators

The ... syntax does two things depending on context.

Spread: Expanding

const nums = [1, 2, 3];
const more = [...nums, 4, 5];  // [1, 2, 3, 4, 5]

const user = { name: "Alice", age: 30 };
const updated = { ...user, age: 31, role: "admin" };
// { name: "Alice", age: 31, role: "admin" }

Spread creates a shallow copy. Nested objects are still shared by reference.

Rest: Collecting

function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4));  // 10

const { name, ...rest } = { name: "Alice", age: 30, role: "admin" };
console.log(rest);  // { age: 30, role: "admin" }

const [first, ...remaining] = [1, 2, 3, 4];
console.log(remaining);  // [2, 3, 4]

Common Pitfalls

Using var instead of const/let. var is function-scoped, which causes bugs in loops.

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// Prints: 3, 3, 3 — not 0, 1, 2. Use let to fix.

Confusing == with ===. Always use ===. The == operator coerces types: 0 == "" is true, 0 === "" is false.

Mutating const objects and thinking they are frozen. const prevents reassignment, not mutation. Use Object.freeze() for shallow immutability.

Forgetting that typeof null returns "object". Check for null explicitly with value === null.

Accidentally creating global variables. Assigning to a variable without declaring it creates a global. Strict mode ("use strict") prevents this.

Key Takeaways

  • Use const by default, let when you need reassignment, never var.
  • JavaScript has seven primitive types. Everything else is an object.
  • Functions come in three forms: declarations (hoisted), expressions, and arrow functions.
  • Template literals with backticks replace string concatenation.
  • Destructuring extracts values from objects and arrays into clean variable bindings.
  • The spread operator expands iterables; the rest operator collects arguments.
  • Always use === for comparisons. Always use strict mode.