// Wrong: == with type coercion// Right: === for strict comparison
Example
// These all evaluate to true (unexpected!)console.log(""==false);// trueconsole.log(0=="");// trueconsole.log(null==undefined);// trueconsole.log("0"==false);// true// Use strict equalityconsole.log(""===false);// falseconsole.log(0==="");// falseconsole.log(null===undefined);// false
Note Rule of thumb: always use === and !==. The only acceptable == use is value == null to check both null and undefined at once.
// 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.30000000000000004console.log(0.1+0.2===0.3);// false// FIX 1: Compare with tolerancefunctionnearlyEqual(a, b, epsilon =Number.EPSILON){returnMath.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 =awaitfetch(`/api/users/${id}`);console.log(await user.json());// fires unpredictably});// FIX 1: Sequential processingfor(const id of userIds){const user =awaitfetch(`/api/users/${id}`);console.log(await user.json());}// FIX 2: Parallel processingconst users =awaitPromise.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)typeofNaN==="number"// true (NaN is a number type)typeof[]==="object"// true (arrays are objects)
Example
// All of these are surprising but correctconsole.log(typeofnull);// "object"console.log(typeofNaN);// "number"console.log(typeof[]);// "object"console.log(typeoffunction(){});// "function"// Better checksconsole.log(value ===null);// null checkconsole.log(Number.isNaN(value));// NaN checkconsole.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.