Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsiTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
JavaScript’s odd-looking results usually follow a small set of rules: operators convert values, truthiness is defined rather than intuitive, and declarations differ in when and where they become available. Knowing those rules makes expressions easier to debug—and helps you choose clearer checks in your own code.
Why do JavaScript values change type or meaning?
JavaScript is dynamically typed: a value has a type, but a variable can hold values of different types over time. Operators may also convert values as they work. The result depends on the operator and the types of its operands, not just on how an expression looks. MDN’s guide to JavaScript data types and data structures explains these conversions and the primitive and object types involved.
1. typeof null returns "object"
null is a primitive value, not an object. Yet typeof null evaluates to "object", a long-standing compatibility oddity. Do not use typeof to test for null; write value === null instead.
2. NaN is a number that is not equal to itself
NaN is a special Number value, often produced when a numeric operation cannot produce a valid number. Thus typeof NaN is "number", but NaN === NaN and NaN == NaN are both false. Test specifically for it with Number.isNaN(value), not an equality comparison. MDN’s equality guide covers this behavior.
#1 Best Overall
3. + can concatenate or add
The plus operator can join strings or add numbers. If primitive conversion produces a string operand, the operation concatenates; otherwise it performs numeric addition. Evaluation proceeds from left to right:
"3" + 4 + 5becomes"34" + 5, then"345".3 + 4 + "5"becomes7 + "5", then"75".
When an expression mixes strings and numbers, make the intended conversion explicit or split the operations into named steps. That avoids relying on a reader to infer which operation + will perform.
4. Loose equality converts values
1 == true is true because loose equality converts the Boolean to the number 1. Strict equality does not make that conversion, so 1 === true is false. Loose equality has several rules—not a blanket rule that every value becomes a number—including special handling for nullish values, strings, BigInts, and objects. For predictable comparisons in ordinary code, use === unless you specifically want loose equality’s conversion behavior. MDN’s comparison guide describes the algorithm and its exceptions.
Rank #2
Why do empty values behave unexpectedly in conditions?
5. Empty arrays and objects are truthy
An empty array ([]) and an empty object ({}) are both truthy. JavaScript’s falsy values are false, 0, -0, 0n, "", NaN, null, and undefined; every other value is truthy. So if (items) checks whether items is truthy, not whether an array contains elements. To check for an empty array, use items.length === 0. MDN’s JavaScript language guide explains truthiness.
6. && and || return operands
These logical operators short-circuit based on truthiness, but they return one of their operand values rather than necessarily returning true or false. For example, 0 || "fallback" returns "fallback", while "ready" && 42 returns 42. This is why an expression using && or || can evaluate to a string, number, object, or undefined.
A common pattern is value && value.property, which avoids reading the property when value is falsy. If the intent is specifically to guard against null or undefined, optional chaining—value?.property—usually states that intent more clearly. The distinction matters when value could validly be another falsy value such as 0 or "".
What do JavaScript declarations and hoisting actually do?
7. var is not block scoped
A var binding belongs to its containing function or, outside a function, the global scope. An if block does not contain it:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →if (true) {
var status = "ready";
}
console.log(status); // "ready"
By contrast, let and const are block scoped. If you need a binding confined to a block, use one of those declarations.
8. “Hoisting” describes different declaration behavior
Hoisting is a shorthand for how declarations are available within their scope; the engine does not literally move source lines. The details depend on the declaration type:
Rank #4
varis initialized toundefinedbefore its assignment runs. Reading it earlier therefore givesundefined, not its later assigned value.letandconstbindings cannot be accessed before execution reaches their declaration. That interval is the temporal dead zone; an earlier access throws aReferenceError.- Function declarations are available earlier in their scope, so a function can be called before its declaration appears in the source.
These differences are documented in MDN’s grammar and types reference. Declare bindings before using them to make control flow and initialization easier to follow.
Which JavaScript oddity is specific to browsers?
9. document.all is a legacy compatibility exception
In browsers, document.all is a host-defined object with unusual behavior retained for web compatibility. It acts like undefined for selected operations: typeof document.all returns "undefined", it is falsy, and document.all == null is true. Yet it is not actually undefined or null. This exception is specified in ECMA-262’s IsHTMLDDA annex; it is not a rule for ordinary objects, and code should not use it as a normal null check.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
How can objects make conversions stranger?
10. Objects can supply their own primitive conversion
When an operation needs a primitive value from an object, JavaScript may consult the object’s [Symbol.toPrimitive] method, then valueOf or toString as appropriate. Arrays and objects can therefore take part in concatenation or equality in ways that are not obvious from their literal syntax.
Best Value
For example, in an expression context, ({} + []) evaluates to "[object Object]": the object converts to that string, the empty array converts to an empty string, and + concatenates them. Parentheses matter here. A brace at the start of a statement can be parsed as a block rather than an object literal, so brace-leading examples can produce a different result depending on context. See MDN’s explanation of object-to-primitive conversion.
A practical way to read surprising expressions
When an expression produces an unexpected value, check it in this order:
- Identify the operand types and whether the operator converts them.
- For
+, follow evaluation from left to right and note when an operand becomes a string. - For conditions and logical operators, ask whether each value is truthy and which operand short-circuiting returns.
- For comparisons, distinguish strict equality from the conversions used by loose equality.
- For a variable access, check its scope, declaration type, and whether execution has reached its initialization.
These questions turn most “weird” results into consequences of a specific rule—and suggest a clearer check or expression when the original code obscures its intent.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

