Skip to main content

Command Palette

Search for a command to run...

JavaScript Ambiguity

A Language That Smiles While Lying to You

Updated
3 min read
JavaScript Ambiguity

Introduction

JavaScript is the only language that will look you in the eye, say "yes, that's valid," and then silently ruin your production build. Every other language would throw an error or refuse to compile - but JavaScript? It coerces types, lies about typeof results, and changes this binding based on invisible rules

The typeof Operator's Big Lies

console.log(typeof [])
// Returns "object"

console.log(typeof {})
// Returns "object"

console.log(typeof Nan)
// Despite being "Not-A-Number"

console.log(typeof null)
// Returns "object"

JavaScript's typeof operator lies blatantly about arrays. An empty array [] is not an object, but typeof [] returns "object" because arrays inherit from Object.prototype. This historical bug from JavaScript's early days trips up developers constantly, same like typeof {}.

Even worse, typeof null returns "object" despite null being a primitive value. This dates back to JavaScript's original implementation where null had an object type tag that was never fixed for backward compatibility.

Array or Object

The real way to check arrays is Array.isArray([]), which returns true.

function isArrayOrObject(value) {
    if (value === null || typeof value !== 'object') {
        return false;
    }
    return Array.isArray(value) || Object.prototype.toString.call(value) === '[object Object]';
}

The real way to check arrays is Array.isArray([]), which returns true.

String Conversion Gotchas

JavaScript loves implicit toString() calls during concatenation.

42 + "0" // 420
[] + {}  // "[object Object]" 
null + "" // null

Number becomes "420" because + coerces the number to string first. But becomes [] + {} becomes "[object Object]" because array toString() joins elements (empty so nothing) and object falls back to default toString.

null + "" is "null", undefined + "" is "undefined".

The Deadly a++a Expression

let a = "1"; 
console.log(a+ +a); 

console.log(('b' + 'a' + +'a'+'a').toLowerCase());
// guess the output

Truthy and Falsy Ambush List

textif([]) console.log("truthy!");  // This runs
!![] === true                   // true
!!0 === false                   // true

Double negation !! value coerces anything to proper boolean: !![] is true, !!0 is false.

Only 6 falsy values exist:

false, 0, "", null, undefined, NaN.

Everything else is truthy, including [] (empty array), {} (empty object), "false" (string), and Infinity.

this Keyword: The Shape-Shifter

Regular functions get dynamic this based on the call site. Arrow functions capture lexical this from the surrounding scope.

textfunction Regular() {
  setTimeout(function() { 
    console.log(this);  // window/global
  }, 0); 
}

const Arrow = () => {
  setTimeout(() => { 
    console.log(this);  // Arrow's lexical this
  }, 0); 
};

Regular.call({x:1});  // logs window
Arrow.call({x:1});    // logs {x:1} if called in object method

Function Definitions Compared

  • function declarations hoist completely (name + body movable to top)

  • function expressions hoist only declaration (body undefined until assignment)

  • Arrow functions are always expressions, no hoisting, no own this/arguments

textgreet();           // Works - fully hoisted
function greet() { console.log("Hi"); }

console.log(fn);   // undefined
var fn = function() {};

sayHi();           // ReferenceError - TDZ
const sayHi = () => console.log("Hi");

Bonus Production Killers

textNaN !== NaN                    // true - use Number.isNaN()
+[] === 0                          // true
+![] === 1                         // true
{} + [] === "[object Object]"      // true  
[] + {} === ""                     // true
JSON.stringify({a: undefined})     // "{}" - drops undefined properties

The Escape Plan

JavaScript doesn't break your code with errors - it transforms it into something subtly wrong that passes tests until production. Use TypeScript for safety, ESLint with strict rules, and always test edge cases. Your future self will thank you.