# JavaScript

> Avoid common JavaScript traps — coercion bugs, this binding, async pitfalls, and mutation surprises.

- Skill: `zangxin75/javascript` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds add zangxin75/javascript`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zangxin75/javascript/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: zangxin75 (https://skillmd.com/u/zangxin75)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/zangxin75/javascript

---


## Equality Traps
- `==` coerces: `"0" == false` is true — use `===` always
- `NaN !== NaN` — use `Number.isNaN()`, not `=== NaN`
- `typeof null === "object"` — check `=== null` explicitly
- Objects compare by reference — `{} === {}` is false

## this Binding
- Regular functions: `this` depends on call site — lost in callbacks
- Arrow functions: `this` from lexical scope — use for callbacks
- `setTimeout(obj.method)` loses `this` — use arrow or `.bind()`
- Event handlers: `this` is element in regular function, undefined in arrow (if no outer this)

## Closure Gotchas
- Loop variable captured by reference — `let` in loop or IIFE to capture value
- `var` hoisted to function scope — creates single binding shared across iterations
- Returning function from loop: all share same variable — use `let` per iteration

## Array Mutation
- `sort()`, `reverse()`, `splice()` mutate original — use `toSorted()`, `toReversed()`, `toSpliced()` (ES2023)
- `push()`, `pop()`, `shift()`, `unshift()` mutate — spread `[...arr, item]` for immutable
- `delete arr[i]` leaves hole — use `splice(i, 1)` to remove and reindex
- Spread and `Object.assign` are shallow — nested objects still reference original

## Async Pitfalls
- Forgetting `await` returns Promise, not value — easy to miss without TypeScript
- `forEach` doesn't await — use `for...of` for sequential async
- `Promise.all` fails fast — one rejection rejects all, use `Promise.allSettled` if need all results
- Unhandled rejection crashes in Node — always `.catch()` or try/catch with await

## Numbers
- `0.1 + 0.2 !== 0.3` — floating point, use integer cents or `toFixed()` for display
- `parseInt("08")` works now — but `parseInt("0x10")` is 16, watch prefixes
- `Number("")` is 0, `Number(null)` is 0 — but `Number(undefined)` is NaN
- Large integers lose precision over 2^53 — use `BigInt` for big numbers

## Iteration
- `for...in` iterates keys (including inherited) — use `for...of` for values
- `for...of` on objects fails — objects aren't iterable, use `Object.entries()`
- `Object.keys()` skips non-enumerable — `Reflect.ownKeys()` gets all including symbols

## Implicit Coercion
- `[] + []` is `""` — arrays coerce to strings
- `[] + {}` is `"[object Object]"` — object toString
- `{} + []` is `0` in console — `{}` parsed as block, not object
- `"5" - 1` is 4, `"5" + 1` is "51" — minus coerces, plus concatenates

## Strict Mode
- `"use strict"` at top of file or function — catches silent errors
- Implicit globals throw in strict — `x = 5` without declaration fails
- `this` is undefined in strict functions — not global object
- Duplicate parameters and `with` forbidden — good riddance

