Note Rule of thumb: always use === and !==. The only acceptable == use is value == null to check both null and undefined at once.
== vs ===loose equalitystrict equalityequality bugcomparison mistake
Losing 'this' Context
syntax
// Problem: extracting a method loses its 'this'// Fix: use bind(), arrow function, or keep method call
example
class Timer {
count = 0;
// BUG: 'this' is undefined in the callback
startBroken() {
setInterval(function () {
this.count++; // TypeError!
}, 1000);
}
// FIX 1: arrow function inherits 'this'
startFixed() {
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
}
// FIX 2: bind the methodconst timer = new Timer();
const increment = timer.startFixed.bind(timer);
Note Arrow functions, .bind(), or storing this in a variable (const self = this) all solve this. Arrow functions are the cleanest modern solution.
this bindinglost thisthis undefinedcontext bindingarrow function this
Closure in Loops (var trap)
syntax
// Bug: var is function-scoped// Fix: use let (block-scoped)
example
// BUG: all callbacks log 3for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Logs: 3, 3, 3// FIX: let is block-scoped, each iteration gets its own ifor (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Logs: 0, 1, 2
Note With var, all iterations share the same variable. By the time callbacks run, the loop is done and i has its final value. This is the #1 reason to use let.
closure in loopvar loop bugsetTimeout looploop variable capture
Floating Point Precision
syntax
// 0.1 + 0.2 !== 0.3
example
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false// FIX 1: Compare with tolerancefunction nearlyEqual(a, b, epsilon = Number.EPSILON) {
return Math.abs(a - b) < epsilon;
}
console.log(nearlyEqual(0.1 + 0.2, 0.3)); // true// FIX 2: Work in integers (cents instead of dollars)const priceInCents = 199; // $1.99const total = priceInCents * 3; // 597 cents = $5.97
Note All numbers in JavaScript are 64-bit floating point (IEEE 754). For financial calculations, use integer arithmetic (cents) or a decimal library.
Note sort(), reverse(), splice() all mutate. This is a major bug source especially in React/Vue state. Use toSorted(), toReversed(), toSpliced() for safe alternatives.
// BUG: forEach ignores async callbacks// FIX: use for...of or Promise.all with map
example
const userIds = [1, 2, 3];
// BUG: forEach does NOT await each call
userIds.forEach(async (id) => {
const user = await fetch(`/api/users/${id}`);
console.log(await user.json()); // fires unpredictably
});
// FIX 1: Sequential processingfor (const id of userIds) {
const user = await fetch(`/api/users/${id}`);
console.log(await user.json());
}
// FIX 2: Parallel processingconst users = await Promise.all(
userIds.map(id => fetch(`/api/users/${id}`).then(r => r.json()))
);
Note forEach returns undefined and does not await the callback. The loop completes immediately while async callbacks run in the background with no error handling.
async forEachforEach async bugawait in loopasync array iteration
Object/Array Reference Sharing
syntax
// Assigning objects copies the reference, not the value
example
const defaults = { theme: "light", notifications: { email: true, sms: false } };
// BUG: both point to the same objectconst userSettings = defaults;
userSettings.theme = "dark";
console.log(defaults.theme); // "dark" (changed!)// FIX: shallow copyconst settings = { ...defaults };
// FIX: deep copy (for nested objects)const deepSettings = structuredClone(defaults);
Note Assignment (=) with objects copies the reference, not the data. Spread (...) is only a shallow copy. Use structuredClone() for nested structures.
reference vs copyobject referenceshallow copy bugshared referencecopy object
typeof null and Other Gotchas
syntax
typeofnull === "object"// true (historic bug)typeof NaN === "number"// true (NaN is a number type)typeof [] === "object"// true (arrays are objects)
example
// All of these are surprising but correct
console.log(typeofnull); // "object"
console.log(typeof NaN); // "number"
console.log(typeof []); // "object"
console.log(typeoffunction(){}); // "function"// Better checks
console.log(value === null); // null check
console.log(Number.isNaN(value)); // NaN check
console.log(Array.isArray(value)); // array check
Note typeof is unreliable for null, arrays, and NaN. Use specialized checks: === null, Array.isArray(), Number.isNaN(), or instanceof for specific types.