🧠 JavaScript Mastery
33+ essential JavaScript concepts every developer should know, inspired by 33-js-concepts.
When to Use This Skill
Use this skill when:
Explaining JavaScript concepts Debugging tricky JS behavior Teaching JavaScript fundamentals Reviewing code for JS best practices Understanding language quirks 1. Fundamentals 1.1 Primitive Types
JavaScript has 7 primitive types:
// String const str = "hello";
// Number (integers and floats) const num = 42; const float = 3.14;
// BigInt (for large integers) const big = 9007199254740991n;
// Boolean const bool = true;
// Undefined let undef; // undefined
// Null const empty = null;
// Symbol (unique identifiers) const sym = Symbol("description");
Key points:
Primitives are immutable Passed by value typeof null === "object" is a historical bug 1.2 Type Coercion
JavaScript implicitly converts types:
// String coercion "5" + 3; // "53" (number → string) "5" - 3; // 2 (string → number)
// Boolean coercion Boolean(""); // false Boolean("hello"); // true Boolean(0); // false Boolean([]); // true (!)
// Equality coercion "5" == 5; // true (coerces) "5" === 5; // false (strict)
Falsy values (8 total): false, 0, -0, 0n, "", null, undefined, NaN
1.3 Equality Operators // == (loose equality) - coerces types null == undefined; // true "1" == 1; // true
// === (strict equality) - no coercion null === undefined; // false "1" === 1; // false
// Object.is() - handles edge cases Object.is(NaN, NaN); // true (NaN === NaN is false!) Object.is(-0, 0); // false (0 === -0 is true!)
Rule: Always use === unless you have a specific reason not to.
- Scope & Closures 2.1 Scope Types // Global scope var globalVar = "global";
function outer() { // Function scope var functionVar = "function";
if (true) { // Block scope (let/const only) let blockVar = "block"; const alsoBlock = "block"; var notBlock = "function"; // var ignores blocks! } }
2.2 Closures
A closure is a function that remembers its lexical scope:
function createCounter() { let count = 0; // "closed over" variable
return { increment() { return ++count; }, decrement() { return --count; }, getCount() { return count; }, }; }
const counter = createCounter(); counter.increment(); // 1 counter.increment(); // 2 counter.getCount(); // 2
Common use cases:
Data privacy (module pattern) Function factories Partial application Memoization 2.3 var vs let vs const // var - function scoped, hoisted, can redeclare var x = 1; var x = 2; // OK
// let - block scoped, hoisted (TDZ), no redeclare let y = 1; // let y = 2; // Error!
// const - like let, but can't reassign const z = 1; // z = 2; // Error!
// BUT: const objects are mutable const obj = { a: 1 }; obj.a = 2; // OK obj.b = 3; // OK
- Functions & Execution 3.1 Call Stack function first() { console.log("first start"); second(); console.log("first end"); }
function second() { console.log("second"); }
first(); // Output: // "first start" // "second" // "first end"
Stack overflow example:
function infinite() { infinite(); // No base case! } infinite(); // RangeError: Maximum call stack size exceeded
3.2 Hoisting // Variable hoisting console.log(a); // undefined (hoisted, not initialized) var a = 5;
console.log(b); // ReferenceError (TDZ) let b = 5;
// Function hoisting sayHi(); // Works! function sayHi() { console.log("Hi!"); }
// Function expressions don't hoist sayBye(); // TypeError var sayBye = function () { console.log("Bye!"); };
3.3 this Keyword // Global context console.log(this); // window (browser) or global (Node)
// Object method const obj = { name: "Alice", greet() { console.log(this.name); // "Alice" }, };
// Arrow functions (lexical this) const obj2 = { name: "Bob", greet: () => { console.log(this.name); // undefined (inherits outer this) }, };
// Explicit binding function greet() { console.log(this.name); } greet.call({ name: "Charlie" }); // "Charlie" greet.apply({ name: "Diana" }); // "Diana" const bound = greet.bind({ name: "Eve" }); bound(); // "Eve"
- Event Loop & Async 4.1 Event Loop console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Output: 1, 4, 3, 2 // Why? Microtasks (Promises) run before macrotasks (setTimeout)
Execution order:
Synchronous code (call stack) Microtasks (Promise callbacks, queueMicrotask) Macrotasks (setTimeout, setInterval, I/O) 4.2 Callbacks // Callback pattern function fetchData(callback) { setTimeout(() => { callback(null, { data: "result" }); }, 1000); }
// Error-first convention fetchData((error, result) => { if (error) { console.error(error); return; } console.log(result); });
// Callback hell (avoid this!) getData((data) => { processData(data, (processed) => { saveData(processed, (saved) => { notify(saved, () => { // 😱 Pyramid of doom }); }); }); });
4.3 Promises // Creating a Promise const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success!"); // or: reject(new Error("Failed!")); }, 1000); });
// Consuming Promises promise .then((result) => console.log(result)) .catch((error) => console.error(error)) .finally(() => console.log("Done"));
// Promise combinators Promise.all([p1, p2, p3]); // All must succeed Promise.allSettled([p1, p2]); // Wait for all, get status Promise.race([p1, p2]); // First to settle Promise.any([p1, p2]); // First to succeed
4.4 async/await
async function fetchUserData(userId) {
try {
const response = await fetch(/api/users/${userId});
if (!response.ok) throw new Error("Failed to fetch");
const user = await response.json();
return user;
} catch (error) {
console.error("Error:", error);
throw error; // Re-throw for caller to handle
}
}
// Parallel execution async function fetchAll() { const [users, posts] = await Promise.all([ fetch("/api/users"), fetch("/api/posts"), ]); return { users, posts }; }
- Functional Programming 5.1 Higher-Order Functions
Functions that take or return functions:
// Takes a function const numbers = [1, 2, 3]; const doubled = numbers.map((n) => n * 2); // [2, 4, 6]
// Returns a function function multiply(a) { return function (b) { return a * b; }; } const double = multiply(2); double(5); // 10
5.2 Pure Functions // Pure: same input → same output, no side effects function add(a, b) { return a + b; }
// Impure: modifies external state let total = 0; function addToTotal(value) { total += value; // Side effect! return total; }
// Impure: depends on external state function getDiscount(price) { return price * globalDiscountRate; // External dependency }
5.3 map, filter, reduce const users = [ { name: "Alice", age: 25 }, { name: "Bob", age: 30 }, { name: "Charlie", age: 35 }, ];
// map: transform each element const names = users.map((u) => u.name); // ["Alice", "Bob", "Charlie"]
// filter: keep elements matching condition const adults = users.filter((u) => u.age >= 30); // [{ name: "Bob", ... }, { name: "Charlie", ... }]
// reduce: accumulate into single value const totalAge = users.reduce((sum, u) => sum + u.age, 0); // 90
// Chaining const result = users .filter((u) => u.age >= 30) .map((u) => u.name) .join(", "); // "Bob, Charlie"
5.4 Currying & Composition // Currying: transform f(a, b, c) into f(a)(b)(c) const curry = (fn) => { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } return (...moreArgs) => curried(...args, ...moreArgs); }; };
const add = curry((a, b, c) => a + b + c); add(1)(2)(3); // 6 add(1, 2)(3); // 6 add(1)(2, 3); // 6
// Composition: combine functions const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);
const addOne = (x) => x + 1; const double = (x) => x * 2;
const addThenDouble = compose(double, addOne); addThenDouble(5); // 12 = (5 + 1) * 2
const doubleThenAdd = pipe(double, addOne); doubleThenAdd(5); // 11 = (5 * 2) + 1
- Objects & Prototypes 6.1 Prototypal Inheritance // Prototype chain const animal = { speak() { console.log("Some sound"); }, };
const dog = Object.create(animal); dog.bark = function () { console.log("Woof!"); };
dog.speak(); // "Some sound" (inherited) dog.bark(); // "Woof!" (own method)
// ES6 Classes (syntactic sugar) class Animal { speak() { console.log("Some sound"); } }
class Dog extends Animal { bark() { console.log("Woof!"); } }
6.2 Object Methods const obj = { a: 1, b: 2 };
// Keys, values, entries Object.keys(obj); // ["a", "b"] Object.values(obj); // [1, 2] Object.entries(obj); // [["a", 1], ["b", 2]]
// Shallow copy const copy = { ...obj }; const copy2 = Object.assign({}, obj);
// Freeze (immutable) const frozen = Object.freeze({ x: 1 }); frozen.x = 2; // Silently fails (or throws in strict mode)
// Seal (no add/delete, can modify) const sealed = Object.seal({ x: 1 }); sealed.x = 2; // OK sealed.y = 3; // Fails delete sealed.x; // Fails
- Modern JavaScript (ES6+) 7.1 Destructuring // Array destructuring const [first, second, ...rest] = [1, 2, 3, 4, 5]; // first = 1, second = 2, rest = [3, 4, 5]
// Object destructuring const { name, age, city = "Unknown" } = { name: "Alice", age: 25 }; // name = "Alice", age = 25, city = "Unknown"
// Renaming const { name: userName } = { name: "Bob" }; // userName = "Bob"
// Nested const { address: { street }, } = { address: { street: "123 Main" } };
7.2 Spread & Rest // Spread: expand iterable const arr1 = [1, 2, 3]; const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
const obj1 = { a: 1 }; const obj2 = { ...obj1, b: 2 }; // { a: 1, b: 2 }
// Rest: collect remaining function sum(...numbers) { return numbers.reduce((a, b) => a + b, 0); } sum(1, 2, 3, 4); // 10
7.3 Modules // Named exports export const PI = 3.14159; export function square(x) { return x * x; }
// Default export export default class Calculator {}
// Importing import Calculator, { PI, square } from "./math.js"; import * as math from "./math.js";
// Dynamic import const module = await import("./dynamic.js");
7.4 Optional Chaining & Nullish Coalescing // Optional chaining (?.) const user = { address: { city: "NYC" } }; const city = user?.address?.city; // "NYC" const zip = user?.address?.zip; // undefined (no error) const fn = user?.getName?.(); // undefined if no method
// Nullish coalescing (??) const value = null ?? "default"; // "default" const zero = 0 ?? "default"; // 0 (not nullish!) const empty = "" ?? "default"; // "" (not nullish!)
// Compare with || const value2 = 0 || "default"; // "default" (0 is falsy)
Quick Reference Card Concept Key Point == vs === Always use === var vs let Prefer let/const Closures Function + lexical scope this Depends on how function is called Event loop Microtasks before macrotasks Pure functions Same input → same output Prototypes proto → prototype chain ?? vs || ?? only checks null/undefined Resources 33 JS Concepts JavaScript.info MDN JavaScript Guide You Don't Know JS