# Mastering Truthy & Falsy in JavaScript: No More Confusion!

> Understand Boolean coercion, loose equality, and the JavaScript comparisons that surprise even experienced developers.

- Author: Aman Kumar Gupta (akgbytes)
- Published: 2025-02-17
- Tags: JavaScript, Language Fundamentals, Type Coercion
- Canonical source: https://akgbytes.in/blogs/mastering-truthy-falsy-values-js

JavaScript developers at every experience level occasionally stumble over **truthy and falsy values**, especially when loose equality (`==`) enters the picture.

Why does `[] == true` evaluate to `false` even though an empty array is truthy? Why does `"" == false` return `true`?

The key is that **Boolean coercion and loose equality are different operations with different rules**. Once you keep those rules separate, the results stop feeling arbitrary.

## What are truthy and falsy values?

A value is **truthy** when `Boolean(value)` produces `true`, and **falsy** when it produces `false`. Conditions such as `if (value)` perform this Boolean coercion automatically.

### The eight ordinary falsy values

JavaScript has eight ordinary falsy values:

1. `false`
2. `0`
3. `-0`
4. `0n` (BigInt zero)
5. `""` (an empty string)
6. `null`
7. `undefined`
8. `NaN`

```javascript
if (NaN) {
  console.log("Am I true?")
} else {
  console.log("NaN is falsy")
}

// Output: NaN is falsy
```

Everything else is truthy, including:

- All ordinary objects, including `{}` and `[]`
- Non-empty strings such as `"hello"`, `"0"`, and `"false"`
- Non-zero numbers, negative numbers, and `Infinity`
- Functions, symbols, and non-zero BigInts
- `new Boolean(false)`, because it creates an object rather than the primitive `false`

> 💡 **A legacy exception:** `document.all` is a special browser object deliberately specified as falsy for old web compatibility. It also behaves like `undefined` in a few other operations. You should never rely on it in application code.

## Truthiness is not equality

This distinction explains most of the confusion:

```javascript
Boolean([]) // true
;[] == true // false
```

The first expression asks JavaScript to convert an array to a Boolean. The second invokes the **abstract equality comparison algorithm**, which may convert both operands through several steps before comparing them.

## How loose equality works

The complete specification algorithm has many branches, but these are the ones you will encounter most often:

- Values of the same type are compared without cross-type coercion.
- `null` and `undefined` are loosely equal to each other and not to other ordinary values.
- When one operand is a Boolean, JavaScript converts that Boolean to a number: `false` becomes `0`, and `true` becomes `1`.
- Strings and numbers are compared numerically, so a numeric string may be converted to a number.
- When an object is compared with a primitive, JavaScript first converts the object to a primitive using its primitive-conversion behavior—typically through `valueOf()` and `toString()`.
- Number and BigInt comparisons follow special numeric rules; JavaScript does not simply convert every BigInt to a Number.

Symbols are not numerically coerced. Two distinct symbols are unequal, while the same symbol reference equals itself:

```javascript
const id = Symbol("id")

id == id // true
Symbol() == Symbol() // false
```

In application code, prefer `===`. Loose equality is worth understanding, but strict equality makes intent clearer and avoids most implicit conversion.

## Decoding the surprising examples

### 1. Empty string and false

```javascript
console.log("" == false) // true
```

The Boolean `false` becomes `0`, and the empty string becomes `0` for the numeric comparison:

```text
"" == false
"" == 0
0 == 0
true
```

### 2. The string "true" and true

```javascript
console.log("true" == true) // false
```

The Boolean `true` becomes `1`, while `Number("true")` produces `NaN`:

```text
"true" == 1
NaN == 1
false
```

`NaN` is unequal to every value, including itself. Use `Number.isNaN(value)` when you need to test for it.

### 3. BigInt and true

```javascript
console.log(12n == true) // false
```

The Boolean becomes the number `1`. JavaScript then compares the numeric values represented by `12n` and `1`. They are different, so the result is `false`—without needing to turn `12n` into a Number first.

### 4. Empty array and true

```javascript
console.log([] == true) // false
```

The Boolean `true` becomes `1`. The array must then become a primitive:

```text
[].valueOf()  // still an array, not a primitive
[].toString() // ""
Number("")    // 0
0 == 1        // false
```

The empty array is still truthy in a condition. Its truthiness simply is not what `==` checks here.

### 5. A populated array and true

```javascript
console.log([1, 2] == true) // false
```

The array becomes the string `"1,2"`. That string cannot be converted to a valid number, so the comparison becomes `NaN == 1`, which is `false`.

### 6. Plain object and true

```javascript
console.log({} == true) // false
```

A plain object usually becomes the string `"[object Object]"`. Its numeric conversion is `NaN`, so it cannot equal `1`.

### 7. Boolean wrapper and false

```javascript
console.log(new Boolean(false) == false) // true
```

`new Boolean(false)` is a truthy object, but loose equality converts the wrapper to its primitive value. The comparison eventually reduces to `0 == 0`, producing `true`.

This contrast is a good reason to avoid Boolean wrapper objects:

```javascript
if (new Boolean(false)) {
  console.log("This still runs because the wrapper is an object.")
}
```

## Key takeaways

- Truthiness answers one question: “What does this value become in a Boolean context?”
- Loose equality answers a different question and may perform type coercion before comparing.
- Objects are truthy in conditions, even when loose equality converts them into surprising primitives.
- Prefer `===` and explicit conversions such as `Boolean(value)` or `Number(value)`.
- Use `Number.isNaN(value)` to detect `NaN`.
- Avoid primitive wrapper constructors such as `new Boolean()`, `new Number()`, and `new String()`.

## Final thought

The next time you encounter this collection of quirks:

```javascript
;[] == false // true
"0" == false // true
null == undefined // true
```

trace the operands through the abstract equality rules instead of asking whether each value is truthy. That small mental shift turns “JavaScript magic” into a predictable sequence of conversions.

Try the examples in your browser console and change one operand at a time. The fastest way to understand coercion is to observe exactly where the comparison changes.