2026-04-05

JavaScript can feel inconsistent until you have the right mental models. This guide focuses on core concepts, practical examples you can run today, and curated links to go deeper when you need.

1) Core mental models (with runnable examples)

Values vs. references

  • Primitives (number, string, boolean, null, undefined, symbol, bigint) are copied by value.
  • Objects and arrays are copied by reference.
let a = { n: 1 };
let b = a;      // b references the same object as a
b.n = 2;
console.log(a.n); // 2

let x = 5;
let y = x;      // y is a copy of x
y++;
console.log(x); // 5

Tip: Create shallow copies with spread or Array methods; deep copy with structuredClone.

const arr = [1, 2, { n: 3 }];
const shallow = [...arr];
const deep = structuredClone(arr);

shallow[2].n = 99;
console.log(arr[2].n); // 99 (shallow copy shares nested object)
console.log(deep[2].n); // 3 (deep copy is independent)

Scope and closures

  • let/const are block-scoped; var is function-scoped.
  • Closures let functions remember variables from their defining scope.
function makeCounter() {
  let count = 0; // private via closure
  return () => ++count;
}

const next = makeCounter();
console.log(next()); // 1
console.log(next()); // 2

this and arrow functions

  • this depends on call-site for regular functions.
  • Arrow functions capture this from the surrounding scope (lexical this).
const user = {
  name: 'Ava',
  say() { console.log('Hi, I am', this.name); }
};

user.say(); // 'Hi, I am Ava'

const say = user.say;
say(); // 'Hi, I am undefined' (or window/global), call-site lost the receiver

const btn = {
  label: 'Save',
  onClick() {
    // Using arrow to keep lexical this
    setTimeout(() => console.log(this.label), 0);
  }
};
btn.onClick(); // 'Save'

Prototypes and classes

  • Methods are shared via the prototype; class is syntax sugar over prototypes.
function Person(name) { this.name = name; }
Person.prototype.greet = function () { return `Hi ${this.name}`; };

const p = new Person('Leo');
console.log(p.greet()); // 'Hi Leo'

class Counter {
  constructor() { this.value = 0; }
  inc() { this.value += 1; }
}
const c = new Counter();
c.inc();
console.log(c.value); // 1

The event loop: tasks vs. microtasks

  • Microtasks (Promise callbacks, queueMicrotask) run before the next macrotask (setTimeout).
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
queueMicrotask(() => console.log('microtask'));
console.log('end');
// Output:
// start
// end
// promise
// microtask
// timeout

2) Asynchronous JavaScript in practice

Promise + async/await

const delay = ms => new Promise(res => setTimeout(res, ms));

async function load() {
  await delay(300);
  return 'done';
}

load().then(console.log); // 'done'

Sequential vs. parallel

// Sequential (slower)
for (const url of urls) {
  const data = await fetchJson(url);
  results.push(data);
}

// Parallel (faster when independent)
const results = await Promise.all(urls.map(fetchJson));

Handling timeouts and cancellation

async function fetchWithTimeout(url, { ms = 5000 } = {}) {
  const ctrl = new AbortController();
  const id = setTimeout(() => ctrl.abort(), ms);
  try {
    const res = await fetch(url, { signal: ctrl.signal });
    if (!res.ok) throw new Error(res.statusText);
    return res.json();
  } finally {
    clearTimeout(id);
  }
}

3) Working in the browser

Event delegation (performant for many items)

document.addEventListener('click', e => {
  const item = e.target.closest('[data-item]');
  if (!item) return;
  console.log('Clicked item id:', item.dataset.item);
});

DOM updates: batch where possible

// Bad: many reflows
for (const data of items) {
  const li = document.createElement('li');
  li.textContent = data.name;
  list.appendChild(li);
}

// Better: fragment
const frag = document.createDocumentFragment();
for (const data of items) {
  const li = document.createElement('li');
  li.textContent = data.name;
  frag.appendChild(li);
}
list.appendChild(frag);

4) Modules and tooling

ES modules (ESM)

// math.js
export function add(a, b) { return a + b; }

// main.js
import { add } from './math.js';
console.log(add(2, 3));
  • In Node: add "type": "module" to package.json or use .mjs.
  • Prefer ESM over CommonJS for tree-shaking and standards alignment.

Arrays and objects: modern operators

const user = { name: 'Ava', settings: { theme: 'dark' } };
const theme = user?.settings?.theme ?? 'light';

const arr = [1, 2, 3];
const doubled = arr.map(n => n * 2).filter(n => n > 3).reduce((a, n) => a + n, 0);
console.log(doubled); // 10

5) Debugging and testing

DevTools tips

  • Breakpoints: right-click line numbers to add conditional breakpoints.
  • Performance tab: profile long tasks and layout thrashing.
  • Console:
console.table(users);
console.time('op'); /* ... */ console.timeEnd('op');

Jest basics

// sum.js
export const sum = (a, b) => a + b;

// sum.test.js
import { sum } from './sum.js';

test('adds', () => {
  expect(sum(2, 3)).toBe(5);
});

Linting/formatting: ESLint + Prettier for consistent style and catching bugs early.

6) Common pitfalls (and fixes)

  • Hoisting: let/const are not accessible before declaration (Temporal Dead Zone).
  • Equality: use === unless you really need coercion.
console.log(false == 0);  // true (coercion!)
console.log(false === 0); // false
  • Floating point:
console.log(0.1 + 0.2);           // 0.30000000000000004
console.log(Number((0.1+0.2).toFixed(2))); // 0.3
  • Mutation:
const state = { items: [] };
const next = { ...state, items: [...state.items, 1] }; // avoid mutating state

7) Performance and memory

  • Avoid creating functions in hot loops; move them out or reuse.
  • Debounce or throttle high-frequency events (scroll, input).
function debounce(fn, ms) {
  let id; return (...args) => { clearTimeout(id); id = setTimeout(() => fn(...args), ms); };
}
  • Clean up: remove event listeners and clear timers when not needed.
  • Use WeakMap/WeakSet for metadata tied to object lifetimes.

8) A focused 10-day study plan

Day 1: Values vs. references, destructuring, spread/rest. Rebuild small utilities. Day 2: Scope, closures. Implement a counter and a memoize function. Day 3: this, call/apply/bind, arrow functions. Fix a "lost this" bug. Day 4: Prototypes and classes. Build a tiny event emitter. Day 5: Promises and async/await. Wrap fetch with retries + timeout. Day 6: Event loop. Predict execution order of mixed tasks/microtasks. Day 7: Array methods. Refactor a for-loop pipeline into map/filter/reduce. Day 8: Modules and tooling. Split code into ESM modules; add ESLint + Prettier. Day 9: DOM patterns. Implement event delegation and a virtualized list. Day 10: Testing + debugging. Add Jest tests and use DevTools performance profiling.

Mini projects (choose 1–2):

  • Debounced search with cancelable fetch.
  • Image gallery with lazy loading and intersection observer.
  • Notes app with persistence (localStorage or IndexedDB).

9) Curated links to go deeper

Docs and references:

  • MDN JavaScript guide and reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript
  • MDN Concurrency model and event loop: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop
  • ECMAScript spec (tc39): https://tc39.es/ecma262/
  • TC39 proposals: https://github.com/tc39/proposals

Deep dives and books:

  • JavaScript.info (comprehensive tutorials): https://javascript.info/
  • Eloquent JavaScript (free online): https://eloquentjavascript.net/
  • You Don’t Know JS Yet (free on GitHub): https://github.com/getify/You-Dont-Know-JS
  • Exploring JS by 2ality: https://exploringjs.com/
  • Jake Archibald on tasks/microtasks: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

Async, performance, engines:

  • MDN Promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
  • MDN structuredClone: https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
  • V8 blog (engine internals/perf): https://v8.dev/

Node and tooling:

  • Node.js docs: https://nodejs.org/api/
  • ESLint: https://eslint.org/
  • Prettier: https://prettier.io/
  • Vite: https://vitejs.dev/

Practice:

  • Exercism JavaScript track: https://exercism.org/tracks/javascript
  • Codewars JavaScript: https://www.codewars.com/collections/javascript

If you pick one habit: after you learn a concept, write a tiny example from scratch and explain it to a rubber duck (or a teammate). That’s how the mental models stick.