JavaScript logoJavaScriptBEGINNER

JavaScript

JavaScript fundamentals cheat sheet with ES6+ syntax, destructuring, modules, closures, scope, and modern best practices with examples.

12 min read
javascriptes6fundamentalsprogramming

Sign in to mark items as known and track your progress.

Sign in

Variables & Data Types

Understanding variables and primitive types

Variable Declaration

Different ways to declare variables

javascript
// const - block-scoped, cannot be reassigned
const name = 'John';

// let - block-scoped, can be reassigned
let age = 30;
age = 31; // OK

// var - function-scoped (avoid in modern JS)
var oldStyle = 'legacy';
💡 Always prefer const by default, use let when reassignment is needed
⚡ const prevents reassignment but not mutation of objects/arrays
📌 Block scope means variables exist only within { }
🚫 Avoid var due to hoisting and function scope issues

Primitive Data Types

JavaScript primitive types

javascript
// String
const text = 'Hello';
const template = `Value: ${text}`;

// Number (integers and floats)
const integer = 42;
const float = 3.14;

// Boolean
const isActive = true;
const isComplete = false;

// undefined & null
let notDefined; // undefined
const empty = null;

// Symbol & BigInt
const sym = Symbol('id');
const big = 123n; // or BigInt(123)
💡 JavaScript has 7 primitive types: string, number, boolean, undefined, null, symbol, bigint
⚡ Primitives are immutable - operations create new values
📌 Use null for intentional empty values, undefined for uninitialized
🔥 BigInt is for integers beyond Number.MAX_SAFE_INTEGER

Type Checking & Conversion

Checking and converting between types

javascript
// Type checking
typeof 'text' // 'string'
typeof 42 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof null // 'object' (legacy bug)
typeof {} // 'object'
typeof [] // 'object'
Array.isArray([]) // true

// Type conversion
String(42) // '42'
Number('42') // 42
Boolean(1) // true
parseInt('42px') // 42
parseFloat('3.14') // 3.14
💡 typeof null returns "object" - a known bug kept for compatibility
⚡ Use Array.isArray() to check for arrays, not typeof
📌 Prefer explicit conversion over implicit coercion
🔥 Falsy values: false, 0, "", null, undefined, NaN

Operators

All JavaScript operators

Arithmetic & Assignment

Math and assignment operators

javascript
// Arithmetic
let x = 10 + 5; // 15
x = 10 - 5; // 5
x = 10 * 5; // 50
x = 10 / 5; // 2
x = 10 % 3; // 1 (remainder)
x = 2 ** 3; // 8 (exponentiation)

// Assignment operators
x += 5; // x = x + 5
x -= 5; // x = x - 5
x *= 5; // x = x * 5
x /= 5; // x = x / 5
x %= 3; // x = x % 3
x **= 2; // x = x ** 2

// Increment/Decrement
x++; // post-increment
++x; // pre-increment
x--; // post-decrement
--x; // pre-decrement
💡 + operator concatenates strings but performs addition with numbers
⚡ Pre-increment (++x) returns new value, post-increment (x++) returns old
📌 Use ** for exponentiation instead of Math.pow()
🔥 Assignment operators provide shorthand for common operations

Comparison & Logical

Comparing values and logical operations

javascript
// Comparison
5 == '5' // true (loose equality)
5 === '5' // false (strict equality)
5 != '5' // false
5 !== '5' // true
5 > 3 // true
5 >= 5 // true
5 < 3 // false
5 <= 5 // true

// Logical
true && false // false (AND)
true || false // true (OR)
!true // false (NOT)

// Short-circuit evaluation
const value = null || 'default'; // 'default'
condition && doSomething();
💡 Always use === and !== to avoid type coercion surprises
⚡ Logical operators use short-circuit evaluation
📌 && returns first falsy or last truthy, || returns first truthy or last falsy
🔥 ?? (nullish coalescing) only checks for null/undefined, not all falsy values

Ternary & Special Operators

Conditional and other special operators

javascript
// Ternary operator
const result = condition ? 'yes' : 'no';

// Optional chaining
const value = obj?.property?.nested;
const result = func?.();

// Nullish coalescing
const value = input ?? 'default';

// Spread operator
const arr = [...array1, ...array2];
const obj = {...obj1, ...obj2};

// Comma operator
let x = (5, 10); // x = 10

// void operator
void functionCall(); // returns undefined
💡 Ternary operator is great for simple conditions, avoid nesting
⚡ Optional chaining prevents "Cannot read property of undefined" errors
📌 Spread creates shallow copies, not deep copies
🔥 Comma operator evaluates all expressions, returns the last one

Control Flow

Controlling program execution

Conditional Statements

if, else, switch statements

javascript
// if/else
if (condition) {
  // code
} else if (otherCondition) {
  // code
} else {
  // code
}

// switch
switch (value) {
  case 'a':
    // code
    break;
  case 'b':
    // code
    break;
  default:
    // code
}
💡 Always use braces {} even for single-line if statements
⚡ Switch uses strict equality (===) for comparisons
📌 Don't forget break in switch cases to prevent fall-through
🔥 Switch is cleaner than multiple if/else for many conditions

Loops

for, while, do-while, for...of, for...in, break, and continue

javascript
for (let i = 0; i < 5; i++) { }
while (condition) { }
for (const item of array) { }    // iterate values
for (const key in object) { }    // iterate keys
💡 for...of iterates VALUES (arrays, strings, Maps, Sets) — use for most loops
⚡ for...in iterates KEYS/properties (objects) — avoid on arrays, use for...of instead
📌 break exits the loop entirely; continue skips to the next iteration
🟢 for...of works on any iterable — arrays, strings, Maps, Sets, generators

Functions

Function declarations, expressions, and arrow functions

Function Declarations, Expressions & Arrows

Three ways to define functions — declarations, expressions, and arrow functions

javascript
// Declaration (hoisted)
function greet(name) { return "Hello " + name; }

// Expression (not hoisted)
const greet = function(name) { return "Hello " + name; };

// Arrow function (short syntax, no own "this")
const greet = (name) => "Hello " + name;
💡 Arrow functions inherit "this" from the parent scope — perfect for callbacks and closures
⚡ Single-expression arrows have implicit return — no curly braces, no return keyword
📌 Don't use arrows for object methods or constructors — they have no own "this"
🟢 Declarations are hoisted; expressions and arrows are NOT — order matters

Parameters & Arguments

Function parameters, defaults, rest, spread

javascript
// Default parameters
function greet(name = 'Guest') {
  return `Hello, ${name}`;
}

// Rest parameters
function sum(...numbers) {
  return numbers.reduce((a, b) => a + b);
}

// Spread arguments
const nums = [1, 2, 3];
sum(...nums);

// Destructuring parameters
function process({ name, age }) {
  // use name and age
}
💡 Default parameters can reference previous parameters
⚡ Rest parameters must be the last parameter
📌 Spread operator expands arrays into individual arguments
🔥 Arrow functions don't have arguments object, use rest parameters

Scope & Closures

Understanding scope and closure behavior

javascript
// Global scope
let global = 'everywhere';

// Function scope
function outer() {
  let outerVar = 'outer';
  
  function inner() {
    let innerVar = 'inner';
    console.log(outerVar); // accessible
  }
}

// Closure
function counter() {
  let count = 0;
  return () => ++count;
}
💡 Functions create new scope, blocks create scope for let/const
⚡ Closures remember variables from outer scope even after function returns
📌 var is function-scoped, let/const are block-scoped
🔥 Closures are commonly used for data privacy and factory functions

Hoisting & Strict Mode

JavaScript hoisting behavior and strict mode

Hoisting

Variable and function hoisting behavior

javascript
// Function hoisting
greet(); // Works!
function greet() {
  console.log('Hello');
}

// Variable hoisting
console.log(x); // undefined
var x = 5;

// let/const not hoisted
// console.log(y); // Error
let y = 5;
💡 Function declarations are fully hoisted and can be called before declaration
⚡ var declarations are hoisted but initialized as undefined
📌 let/const are in "temporal dead zone" until declaration
🔥 Always declare variables at the top of their scope to avoid confusion

Strict Mode

Using strict mode for safer JavaScript

javascript
// Enable strict mode
'use strict';

// Prevents accidental globals
// mistypedVariable = 17; // Error

// Prevents duplicate parameters
// function sum(a, a, c) {} // Error

// Makes eval safer
// eval has its own scope in strict mode
💡 Always use strict mode to catch common mistakes
⚡ Modules and classes are automatically in strict mode
📌 Strict mode must be first statement in script or function
🔥 Strict mode makes this undefined in regular functions

Objects & Arrays

Working with objects and arrays

Object Basics

Creating and manipulating objects

javascript
// Object literal
const person = {
  name: 'John',
  age: 30,
  greet() {
    return `Hello, I'm ${this.name}`;
  }
};

// Accessing properties
person.name; // 'John'
person['age']; // 30

// Adding/modifying
person.email = 'john@example.com';
delete person.age;
💡 Use dot notation for simple keys, brackets for dynamic/special keys
⚡ Object.assign and spread create shallow copies only
📌 in operator checks prototype chain, hasOwnProperty doesn't
🟢 See also: Array Methods sheet (map, filter, reduce) and String Methods sheet (split, slice, replace)

Array Basics

Creating and manipulating arrays

javascript
// Array creation
const arr = [1, 2, 3];
const mixed = [1, 'two', true, null];

// Accessing elements
arr[0]; // 1
arr.at(-1); // 3 (last element)

// Length and modification
arr.length; // 3
arr.push(4); // add to end
arr.pop(); // remove from end
arr.unshift(0); // add to beginning
arr.shift(); // remove from beginning
💡 Use at() for negative indexing to access from end
⚡ push/pop are faster than unshift/shift
📌 splice mutates original, slice returns new array
🟢 See also: Array Methods sheet for map, filter, reduce, find, sort, and 20+ more methods

Map & Set

Key-value collections and unique value sets beyond plain objects and arrays

javascript
// Map — key-value pairs (any key type)
const map = new Map();
map.set("name", "Alice");
map.get("name");  // "Alice"

// Set — unique values only
const set = new Set([1, 2, 2, 3]);
// Set { 1, 2, 3 }
💡 Map accepts ANY key type (objects, functions, numbers) — Object only accepts strings/symbols
⚡ [...new Set(array)] is the fastest way to deduplicate an array
📌 Map preserves insertion order and has .size — Object needs Object.keys().length
🟢 Use Map for dynamic key-value data; use Object for structured data with known properties
mapsetcollections

JSON

Parse and serialize data with JSON.parse() and JSON.stringify()

javascript
// Parse JSON string → object
const data = JSON.parse('{"name":"Alice","age":30}');

// Serialize object → JSON string
const json = JSON.stringify({ name: "Alice", age: 30 });
💡 JSON.stringify(obj, null, 2) pretty-prints with indentation — great for debugging
⚡ Use structuredClone() instead of JSON parse/stringify for deep cloning — handles Dates, Maps, Sets
📌 JSON cannot serialize undefined, functions, Symbols, or circular references
🟢 response.json() in fetch is a shorthand for JSON.parse(await response.text())
jsonparsestringify

The "this" Keyword

Understanding this binding in different contexts

this Binding Rules

How this is determined in different contexts

javascript
// Global context
console.log(this); // window (browser) or global (Node)

// Object method
const obj = {
  name: 'Object',
  greet() {
    console.log(this.name); // 'Object'
  }
};

// Function context
function regular() {
  console.log(this); // window or undefined (strict)
}

// Arrow functions
const arrow = () => {
  console.log(this); // inherits from parent
};
💡 Arrow functions don't have own this, they inherit from parent
⚡ In strict mode, this is undefined in regular functions
📌 Method borrowing loses this context
🔥 Event handlers set this to the element that triggered event

Explicit this Binding

Using call, apply, and bind

javascript
// call - invoke with specific this
func.call(thisArg, arg1, arg2);

// apply - invoke with array of args
func.apply(thisArg, [arg1, arg2]);

// bind - create bound function
const bound = func.bind(thisArg);
bound(arg1, arg2);
💡 call and apply invoke immediately, bind returns new function
⚡ Use call for known args, apply for array of args
📌 bind is commonly used to fix this in event handlers
🔥 Arrow functions are often simpler than bind for callbacks

Prototypes & Inheritance

JavaScript prototypal inheritance

Prototype Basics

Understanding the prototype chain

javascript
// Every object has a prototype
const obj = {};
obj.__proto__; // Object.prototype

// Constructor prototype
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return `Hello, I'm ${this.name}`;
};

const john = new Person('John');
john.greet(); // method from prototype
💡 Prototype chain: object -> prototype -> prototype -> null
⚡ Methods on prototype are shared, properties usually on instance
📌 Use Object.getPrototypeOf() instead of __proto__
🔥 instanceof checks entire prototype chain

Prototypal Inheritance

Implementing inheritance with prototypes

javascript
// Constructor inheritance
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  return `${this.name} makes a sound`;
};

function Dog(name, breed) {
  Animal.call(this, name); // super constructor
  this.breed = breed;
}

// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
💡 Use Object.create() to set up prototype chain
⚡ Don't forget to reset constructor after changing prototype
📌 Call parent constructor with ParentConstructor.call(this)
🔥 Classes (ES6) provide cleaner syntax for same behavior

Classes

ES6 class syntax and inheritance

Class Basics

Creating and using classes

javascript
// Class declaration
class Person {
  constructor(name) {
    this.name = name;
  }
  
  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

const john = new Person('John');
john.greet();
💡 Classes are syntactic sugar over prototypes
⚡ Class methods are non-enumerable by default
📌 Class declarations are not hoisted
🔥 Always use new with classes, calling without throws error

Class Inheritance

Extending classes with inheritance

javascript
// Class inheritance
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    return `${this.name} makes a sound`;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // call parent constructor
    this.breed = breed;
  }
  
  bark() {
    return 'Woof!';
  }
}
💡 super() must be called before using this in constructor
⚡ Use super.method() to call parent methods
📌 Private fields start with # and are truly private
🔥 Static methods are inherited but not instance methods

Error Handling

Handling errors and exceptions

Try/Catch/Finally

Handling exceptions with try/catch

javascript
// Basic try/catch
try {
  // code that may throw
  riskyOperation();
} catch (error) {
  console.error(error.message);
} finally {
  // always runs
  cleanup();
}
💡 finally block runs whether error occurs or not
⚡ Catch specific error types with instanceof
📌 Rethrow errors when you can't fully handle them
🟢 See also: Async JavaScript sheet for async error handling with try/catch + await

Throwing Errors

Creating and throwing custom errors

javascript
// Throwing errors
throw new Error('Something went wrong');

// Custom error class
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

throw new ValidationError('Invalid input');
💡 Create custom error classes for specific error types
⚡ Include relevant data in custom errors for better debugging
📌 Error.cause helps chain errors with context
🔥 Always throw Error objects, not strings

Error Types

Common JavaScript error types

javascript
// Common error types
new Error('Generic error');
new TypeError('Wrong type');
new ReferenceError('Not defined');
new SyntaxError('Invalid syntax');
new RangeError('Out of range');
new URIError('URI malformed');
new EvalError('Eval error');
💡 Use specific error types for clearer error handling
⚡ TypeError is most common runtime error
📌 SyntaxError usually can't be caught (parse-time)
🔥 AggregateError useful for Promise.allSettled() results

Modern JavaScript (ES6+)

Modern JavaScript features

Spread & Rest Operators (...)

Expand iterables and collect remaining elements with the ... syntax

javascript
// Spread — expand into individual elements
const arr = [1, 2, 3];
const copy = [...arr];
const merged = [...arr, 4, 5];
const clone = { ...user, age: 31 };

// Rest — collect remaining elements
const [first, ...rest] = [1, 2, 3, 4];
function sum(...nums) { return nums.reduce((a, b) => a + b); }
💡 Spread (...) expands; Rest (...) collects — same syntax, opposite operations
⚡ { ...obj } and [...arr] create SHALLOW copies — nested objects are still references
📌 const { password, ...safeUser } = user is the cleanest way to remove a property immutably
🟢 Rest params (...args) replace the old "arguments" object — they give a real array
spreadrestes6

Template Literals

String templates and tagged templates

javascript
// Template literals
const name = 'John';
const greeting = \`Hello, \${name}!\`;

// Multi-line strings
const text = \`Line 1
Line 2
Line 3\`;

// Tagged templates
const styled = css\`color: red;\`;
💡 Template literals preserve whitespace and newlines
⚡ Use String.raw for paths and regex patterns
📌 Tagged templates can process and transform strings
🟢 See also: String Methods sheet for split, slice, replace, trim, and more

Destructuring

Extracting values from arrays and objects

javascript
// Object destructuring
const { name, age } = person;
const { x: newX, y: newY } = point;

// Array destructuring
const [first, second] = array;
const [head, ...tail] = array;

// Function parameters
function greet({ name, age = 18 }) {
  // use name and age
}
💡 Use default values to handle undefined properties
⚡ Rest operator must be last in destructuring pattern
📌 Destructuring is shallow, not deep copying
🔥 Great for extracting multiple return values

Modules

ES6 module system

javascript
// Named exports
export const name = 'John';
export function greet() {}

// Default export
export default class User {}

// Importing
import User from './user.js';
import { name, greet } from './utils.js';
import * as utils from './utils.js';
💡 Use named exports for utilities, default for main class/component
⚡ Dynamic imports enable code splitting and lazy loading
📌 Module code runs only once, regardless of imports
🔥 Modules are always in strict mode

Optional Chaining & Nullish Coalescing

Safe property access and default values

javascript
// Optional chaining
const city = user?.address?.city;
const result = obj.method?.();
const item = arr?.[index];

// Nullish coalescing
const value = input ?? 'default';
const port = process.env.PORT ?? 3000;
💡 ?. stops evaluation and returns undefined if null/undefined
⚡ ?? only replaces null/undefined, not other falsy values
📌 Use ?? for numeric values that could be 0
🔥 Combine ?. and ?? for safe access with defaults

Recent Additions (ES2023+)

Newer JavaScript features added since 2023: immutable arrays, grouping, structured clone, iterator helpers, and more.

javascript
// === Immutable array methods (ES2023) ===
const nums = [3, 1, 2]
nums.toSorted()           // [1, 2, 3]  (nums unchanged)
nums.toReversed()         // [2, 1, 3]
nums.toSpliced(1, 1, 99)  // [3, 99, 2]
nums.with(0, 100)         // [100, 1, 2]

// === Object.groupBy (ES2024) ===
const users = [
  { name: 'Alice', role: 'admin' },
  { name: 'Bob',   role: 'user' },
  { name: 'Carol', role: 'admin' },
]
Object.groupBy(users, u => u.role)
// { admin: [Alice, Carol], user: [Bob] }

// === Promise.withResolvers (ES2024) ===
const { promise, resolve, reject } = Promise.withResolvers()

// === structuredClone (deep clone — almost anything) ===
const copy = structuredClone({ date: new Date(), nested: [1, 2] })

// === Iterator helpers (ES2025) ===
const evens = nums.values().filter(n => n % 2 === 0).take(3).toArray()
💡 Immutable array methods (toSorted, toReversed, toSpliced, with) return new arrays — don't mutate
⚡ Object.groupBy / Map.groupBy replace manual reduce-based grouping
📌 Promise.withResolvers exposes resolve/reject outside the executor — handy for adapters
🔥 structuredClone deep-clones almost anything (Date, Map, Set, ArrayBuffer, nested objects)