Javascript Variables, Data Types & Operators Booleans, null, and undefined

When I was starting out, null and undefined were the two values I kept glossing over. Both meant “nothing,” right? What was there to actually learn? And booleans: true and false, even simpler. Just yes or no.

That attitude cost me plenty of debugging hours later. Because it turns out JavaScript treats a whole handful of values, including your empty string, your zero, and your null, as secretly false inside an if statement. And if you don’t know the full list, you’ll write code that quietly misbehaves in ways that are genuinely hard to trace.

We’ve been building through this series piece by piece. We covered all eight JavaScript data types, then went deep on strings and numbers. In the numbers article, we touched on NaN. Today you’ll see why it shows up on the falsy values list, and what that actually means for your code.

By the time you finish this, you’ll know exactly what the six falsy values in JavaScript are, you’ll finally understand the real difference between null and undefined, and you’ll see how all of it flows into the conditional logic you write every day. Let’s get into it.


01What a Boolean Actually Is in JavaScript

A boolean is the simplest data type you’ll work with. It has exactly two possible values: true and false. No numbers, no strings, just those two.

You can assign them to a variable directly:

let isLoggedIn = true;
let hasPremium = false;

console.log(isLoggedIn);         // true
console.log(hasPremium);         // false
console.log(typeof isLoggedIn);  // "boolean"

But most of the time, you don’t write booleans by hand. They’re the result of a comparison or a check:

let age = 20;

console.log(age >= 18);    // true
console.log(age === 30);   // false
console.log(age !== 15);   // true

let name = "Alex";
console.log(name.length > 0);  // true

Every comparison expression evaluates to either true or false. That’s what makes booleans so central to how conditional logic works in JavaScript.

Now here’s something worth knowing early: JavaScript has a built-in Boolean() function that converts any value into its boolean equivalent. This is exactly how JavaScript decides what “counts” as true or false inside an if statement:

console.log(Boolean(1));         // true
console.log(Boolean(0));         // false
console.log(Boolean("hello"));   // true
console.log(Boolean(""));        // false
console.log(Boolean(null));      // false
console.log(Boolean(undefined)); // false

Some values convert to true, others to false. The ones that convert to false are called falsy values. That’s what we’re mapping out next.


02The Six Falsy Values in JavaScript: The Complete List

JavaScript has a specific set of values that are considered falsy. Everything else, every other value in the entire language, is truthy. Knowing this list by heart will save you from bugs you really don’t want to track down at 11pm.

Here they are:

false

The boolean false

The literal boolean value false. The obvious one.

0

The number zero

Both 0 and -0 are falsy. Any other number is truthy.

""

Empty string

A string with zero characters. Even a single space " " is truthy.

null

Intentional emptiness

A value you explicitly assign to mean "nothing here on purpose."

undefined

Uninitialized

A variable declared but never given a value. JavaScript sets this automatically.

NaN

Not a Number

Result of invalid math operations. Covered in the numbers article.

Let’s verify all of them with Boolean():

// All six falsy values in JavaScript
console.log(Boolean(false));      // false
console.log(Boolean(0));          // false
console.log(Boolean(""));         // false
console.log(Boolean(null));       // false
console.log(Boolean(undefined));  // false
console.log(Boolean(NaN));        // false

// Everything else is truthy:
console.log(Boolean(1));          // true
console.log(Boolean("hello"));    // true
console.log(Boolean([]));         // true  (yes, an empty array!)
console.log(Boolean({}));         // true  (yes, an empty object too!)

That last bit will come up again. Hold onto it for now. Let’s walk through each falsy value properly.

false: The Straightforward One

The literal boolean false is falsy. You’ll use it when you want to explicitly represent a “no” or “off” state:

let isDarkMode = false;

if (isDarkMode) {
  console.log("Dark mode is on");
} else {
  console.log("Light mode is active");  // This one runs
}

0 (and -0): Zero Is Always Falsy

The number zero is falsy. This one catches people out constantly when they’re working with counts, scores, or quantities:

let score = 0;

// This looks right but has a problem:
if (score) {
  console.log("Score: " + score);
} else {
  // This runs even though score IS defined — it's just zero
  console.log("No score yet");
}

// If you want to check whether score exists as a number (including zero):
if (score !== undefined && score !== null) {
  console.log("Score: " + score);  // Now this works correctly
}

Also worth mentioning: -0 is falsy too. It’s a rare edge case in JavaScript’s floating-point system. You probably won’t encounter it often, but it’s good to know it exists.

“” : Zero Characters, Zero Truth

An empty string, whether written as "", '', or a template literal with nothing inside it, is falsy. A string with even one character (including a space) is truthy:

let username = "";

if (username) {
  console.log("Welcome, " + username);
} else {
  console.log("Please enter your name");  // This runs
}

// Watch these carefully:
console.log(Boolean(""));     // false — empty string
console.log(Boolean(" "));    // true  — has one space character
console.log(Boolean("0"));    // true  — the STRING "0", not the number 0
console.log(Boolean("false")); // true  — a non-empty string

That last two are where people get surprised. The string "0" is truthy because it has a character in it. Only the number 0 is falsy. We’ll revisit this when we cover type coercion in the next article.

NaN: The Math That Went Wrong

We covered NaN in depth in the numbers article. Short version: it appears when a math operation produces something that isn’t a valid number, like trying to parse a word as a number:

let result = parseInt("hello");
console.log(result);          // NaN
console.log(Boolean(result)); // false

if (result) {
  console.log("Got a valid number");
} else {
  console.log("That wasn't a valid number");  // This runs
}

// Also NaN from invalid math:
console.log(Boolean(0 / 0));    // false
console.log(Boolean(Math.sqrt(-1)));  // false

Now let’s get into the two that deserve the most real estate in this article: null and undefined.


03null vs undefined in JavaScript: The Difference Explained

These two are so commonly confused that I want to slow down here. Both are falsy. Both represent “no value.” But they come from completely different situations and they mean different things.

null: You Decided There’s Nothing Here

null is intentional. You, the developer, put it there. It means: “this variable exists, it’s set up, and I’m making a deliberate decision that it holds no value right now.”

Think of it like an empty labeled box you placed on the shelf. The box is real. You put it there on purpose. You just chose not to put anything inside it yet.

let selectedUser = null;  // No user selected yet — intentional

// Later, when someone logs in:
// selectedUser = { name: "Alex", id: 42 };

console.log(selectedUser);          // null
console.log(typeof selectedUser);   // "object"  ← we'll talk about this quirk shortly

Common situations where you’ll use null: initializing a variable before your data arrives, clearing something on purpose (like resetting a form or canceling a selection), or signaling that an API search returned no matching results.

undefined: JavaScript Said There’s Nothing Here

undefined means a variable was declared but was never given a value. You didn’t set it to anything. JavaScript automatically fills it in with undefined.

let currentPage;  // Declared but not assigned anything
console.log(currentPage);          // undefined
console.log(typeof currentPage);   // "undefined"

// Also shows up when you access a property that doesn't exist:
let user = { name: "Sam" };
console.log(user.email);   // undefined — the "email" property was never set

// And when a function returns nothing:
function doNothing() {}
let result = doNothing();
console.log(result);  // undefined

The core distinction is who set it. If you intentionally said “nothing here”: that’s null. If you never said anything and JavaScript filled in the blank: that’s undefined.

null

Intentional

Who sets it

You, the developer. On purpose.

What it means

"This variable exists and I'm explicitly saying it holds no value right now."

typeof result

typeof null → "object"

Common use

Pre-initialization, clearing state, empty API results.

undefined

Automatic

Who sets it

JavaScript, automatically.

What it means

"This variable was declared but was never given a value."

typeof result

typeof undefined → "undefined"

Common use

Missing object properties, uninitialized variables, functions with no return.

Equality Between null and undefined

Here’s something to keep in mind now, even though we’ll go much deeper on it in the type coercion article:

// Loose equality (==): null and undefined ARE equal to each other
console.log(null == undefined);   // true

// Strict equality (===): they are NOT the same type, so NOT equal
console.log(null === undefined);  // false

// Neither null nor undefined equals anything else with loose equality:
console.log(null == false);  // false
console.log(null == 0);      // false
console.log(null == "");     // false

console.log(undefined == false); // false
console.log(undefined == 0);     // false

With loose equality (==), null and undefined only equal each other, nothing else. This is actually a useful behavior. If you write value == null, it catches both null and undefined in one check. We’ll revisit this fully in the operators article.


04The typeof null Quirk: JavaScript’s Most Famous Bug

We saw this in the comparison cards above, but it deserves its own spotlight because it surprises nearly everyone the first time.

console.log(typeof null);        // "object"    ← this looks wrong
console.log(typeof undefined);   // "undefined" ← this is correct
console.log(typeof false);       // "boolean"
console.log(typeof 0);           // "number"

typeof null returns "object". That’s a genuine bug that has been in the language since 1995.

Here’s what happened: the original JavaScript implementation stored type information in the low bits of values in memory. The type code for objects was 0. And null was represented internally as a null pointer, which was also stored as all zeros. When the type check ran, it saw that zero tag and said “that’s an object.” By the time people noticed, so much existing code depended on this behavior that fixing it would have broken the web. So it stayed.

⚠️

Practical rule: Never use typeof someVar === "object" as a way to check for null. That check returns true for null, which is almost certainly not what you want. Always check for null directly with someVar === null.

let data = null;

// This catches null incorrectly — don't do this:
if (typeof data === "object") {
  console.log("This runs for null too!");  // Runs even though data is null
}

// The correct way to check for null:
if (data === null) {
  console.log("data is specifically null");  // Correct
}

// To catch both null and undefined in one check:
if (data == null) {
  console.log("data is null or undefined");  // Catches both with loose equality
}

// To check if something is a real non-null object:
if (data !== null && typeof data === "object") {
  console.log("This is a real object");  // Safe combined check
}

05How Falsy Values Work Inside Conditional Logic

This is where everything connects to code you write every single day. JavaScript evaluates every value you put inside an if condition by converting it to boolean. That conversion uses the same rules we just covered.

let input = "";

// JavaScript converts "" to Boolean("") which is false, so else runs:
if (input) {
  console.log("User typed something");
} else {
  console.log("Input is empty");  // This runs
}

let count = 0;

// JavaScript converts 0 to Boolean(0) which is false:
if (count) {
  console.log("Count: " + count);
} else {
  console.log("Count is zero or not set");  // This runs
}

let response = null;

// null is falsy, so the else branch runs:
if (response) {
  console.log("Got a response");
} else {
  console.log("No response yet");  // This runs
}

This gives you a shorthand: instead of writing if (input !== "" && input !== null && input !== undefined), you can often just write if (input). But use that shorthand carefully. If a value like 0 or an empty string is legitimately valid in your context, the falsy shorthand will treat it as “missing.”

Click any value below to see how JavaScript evaluates it inside an if statement:

Truthy / Falsy Tester

Falsy values (click to test):

false0“”nullundefinedNaN-00n

Truthy values (click to test):

true1-1“hello”“0”“false”[]{}Infinity

Click a value above to test it

Short-Circuit Evaluation: How && and || Use Falsy Logic

The && and || operators don’t just return true or false. They return one of the actual values, depending on which one they stopped at. This is called short-circuit evaluation, and it’s built directly on top of falsy logic:

// OR (||): returns the FIRST truthy value it encounters, or the last value
console.log("" || "default");       // "default"  — empty string is falsy
console.log(null || "fallback");    // "fallback"  — null is falsy
console.log("Alex" || "default");   // "Alex"      — found truthy, stops here
console.log(0 || 42);              // 42           — 0 is falsy, returns 42

// AND (&&): returns the FIRST falsy value, or the last value if all are truthy
console.log(true && "hello");       // "hello"  — all truthy, returns last value
console.log(false && "hello");      // false    — first falsy, stops immediately
console.log(null && doSomething()); // null     — never even calls doSomething()

The || pattern for default values is everywhere in real JavaScript codebases. The one limitation: it breaks down when 0 or "" are legitimate values in your data, since those are falsy and would be skipped. The ?? nullish coalescing operator was introduced specifically to handle that case. We’ll cover it fully in the operators article.


06The Boolean() Function and the !! Double Negation

Boolean() is the clean, explicit way to convert any value to its boolean equivalent. We’ve been using it throughout this article:

console.log(Boolean(42));        // true
console.log(Boolean(""));        // false
console.log(Boolean(undefined)); // false
console.log(Boolean([]));        // true  — empty array!
console.log(Boolean(null));      // false
console.log(Boolean("0"));       // true  — non-empty string

The other common way you’ll see in real codebases is double negation with !!. A single ! flips the boolean. A second ! flips it back, giving you the actual truthiness of the original value:

console.log(!0);     // true  — 0 is falsy, so !0 is true
console.log(!!0);    // false — back to the truthiness of 0

console.log(!!"");   // false — empty string is falsy
console.log(!!"hi"); // true  — non-empty string is truthy

console.log(!!null);       // false
console.log(!!undefined);  // false
console.log(!!42);         // true
console.log(!![]);         // true — empty array is truthy

Boolean(value) and !!value do the exact same thing. The double negation is just shorter. You’ll see it more in production code, so it’s worth recognizing when you encounter it.

💡

When is this actually useful? When you need to store or return a guaranteed boolean type, not just something truthy or falsy. For example, a function that should return exactly true or false (not "hello" or null). Using !! or Boolean() guarantees you get the real boolean type.


07Truthy Values: The Ones That Surprise Beginners

The rule is simple: anything not on the falsy list is truthy. But a few truthy values genuinely catch people off guard:

Value Why it’s truthy (and where the surprise comes from)
[] An empty array is truthy. It’s an object in memory, even with no elements inside it.
{} An empty object is truthy. Same reason. The object reference exists, even if it has no properties.
“0” The string "0" is truthy. It’s a non-empty string. The number 0 is falsy, but the string "0" is not.
“false” The string "false" is truthy. It’s not the boolean false, it’s a string that happens to contain the word “false.”
-1 Any non-zero number is truthy. This includes all negative numbers.
Infinity Truthy. It’s a non-zero number value in JavaScript’s type system.
new Date() Any object is truthy, including Date instances, arrays, and other built-in objects.

The empty array and empty object are the most common sources of bugs here. If you want to check whether an array actually has items, you need to look at its .length property, not just the array itself:

let items = [];

// This runs even when the array is empty — probably not what you want:
if (items) {
  console.log("Array exists");  // Runs even for an empty []
}

// The right way to check if the array has items:
if (items.length > 0) {
  console.log("Has items");
} else {
  console.log("Array is empty");  // Correct
}

// Same pattern for strings — check length if an empty string matters:
let message = "";
if (message.length > 0) {
  console.log("Has message content");
} else {
  console.log("Message is empty");  // Correct
}

08When to Use null vs undefined in Your Own Code

This is a question that comes up once you’ve understood the difference: okay, so when should I actually use each one in my own variables?

A practical rule that works well in most situations:

  • Use null when you want to explicitly signal that a variable has no value right now. You’re making a deliberate statement: “I know this exists, and I’m intentionally leaving it empty.”
  • Let undefined happen on its own. Don’t manually assign it. If something is undefined, it means you haven’t initialized it yet. That’s JavaScript’s job.
// Good: null to signal "no selection yet"
let activeProduct = null;
// Later: activeProduct = { id: 1, name: "Laptop" };

// Not ideal: manually assigning undefined
let something = undefined;  // Avoid this

// Better: just declare without a value
let something2;  // JavaScript gives it undefined automatically

There’s also a meaningful distinction in function arguments. If someone passes undefined, they didn’t provide the argument. If they pass null, they did provide it but explicitly said “nothing.”

function greet(name) {
  if (name === undefined) {
    // They didn't pass anything
    console.log("No argument was provided");
  } else if (name === null) {
    // They passed null — intentional empty value
    console.log("Name was explicitly set to null");
  } else {
    console.log("Hello, " + name);
  }
}

greet();           // "No argument was provided"
greet(null);       // "Name was explicitly set to null"
greet("Maria");    // "Hello, Maria"

That distinction is subtle, but it shows up in real library APIs. Worth knowing.


09Putting It Together: Checking User Input with Falsy Logic

Let’s write something real. A form validation function that handles all the cases we’ve talked about. This is the kind of code where falsy knowledge pays off directly:

function validateRegistration(username, age, bio) {
  let errors = [];

  // Username: falsy check catches "", null, and undefined all at once
  if (!username) {
    errors.push("Username is required");
  } else if (username.trim().length < 3) {
    errors.push("Username must be at least 3 characters");
  }

  // Age: !age would flag 0 too, which is fine here (0 is invalid age)
  // But we also check for non-numeric values (NaN from parseInt)
  let parsedAge = parseInt(age, 10);
  if (!parsedAge || parsedAge < 13) {
    errors.push("Must be at least 13 years old");
  }

  // Bio is optional — null means "intentionally left blank," that's fine
  // But if a string was provided, it shouldn't be just whitespace
  if (typeof bio === "string" && !bio.trim()) {
    errors.push("Bio can't be just whitespace if provided");
  }

  return errors;
}

// Test cases:
console.log(validateRegistration("", null, "   "));
// ["Username is required", "Must be at least 13 years old", "Bio can't be just whitespace if provided"]

console.log(validateRegistration("Alex", 25, null));
// [] — valid, bio is intentionally null (optional field)

console.log(validateRegistration("Jo", "not-a-number", ""));
// ["Username must be at least 3 characters", "Must be at least 13 years old", "Bio can't be just whitespace if provided"]

console.log(validateRegistration("Sam", 20, undefined));
// [] — valid, bio not provided at all

See how the logic uses !username for the first check, but switches to explicit type checks for the bio? That's the judgment call. Use falsy shortcuts where the falsy values genuinely mean "invalid," and use explicit checks where they might legitimately appear in valid data.


10Quick Reference: The Full Falsy Values List in JavaScript

Save this somewhere handy. This is the complete picture of every falsy value in JavaScript:

Value Type Falsy? Notes
false boolean FALSY The literal boolean false.
0 number FALSY Zero. Includes -0.
0n BigInt FALSY BigInt zero. See the numbers article for BigInt context.
"" string FALSY Empty string. Includes '' and empty template literals.
null null FALSY Intentional absence. You set this.
undefined undefined FALSY Uninitialized. JavaScript sets this.
NaN number FALSY Result of invalid math. Not equal to itself: NaN !== NaN.

And the golden rule for everything else: if it isn't on that list, it's truthy. Including [], {}, "0", "false", -1, Infinity, and any object at all.


11What's Next in the Series

The next article is one I'm genuinely excited about: Type Coercion. This is where JavaScript's implicit conversion rules come fully into view, and it builds directly on what you just learned.

You'll see why "5" + 3 gives "53" while "5" - 3 gives 2. You'll understand the Abstract Equality algorithm, and why == and === behave so differently in practice. The falsy values we covered today will show up throughout that discussion.

If you missed any of the earlier groundwork, the data types overview gives you the full type system picture, and the variables article covers how declared-but-unassigned variables end up as undefined in the first place.

Falsy values are one of those things that seem minor until they're not. Now you know the full list, you know the difference between null and undefined, and you know how all of it feeds into your conditions. That's foundational knowledge that'll stay useful for as long as you write JavaScript.

Booleans, null, and undefined

Choose a difficulty to load your challenge

Loading challenge…

Ready to Practice?

Select a difficulty level above to load your challenge.

Easy

1
Live Preview
Great Work!