# Missing Values: null, undefined, ?. and ??

Real data has holes, so learn the difference between undefined and null, read a property that might be missing with ?., and give a missing value a default with ?? instead of ||.

Apprentice · JavaScript · Updated November 1, 2026

## After this lesson you can

- Tell null from undefined and read a property that might be missing with ?.
- Give a missing value a default with ??, and choose between ?? and ||

## Key takeaways

- undefined means nothing was ever put there, such as a missing property. null means someone left it empty on purpose.
- Reading a property of undefined or null stops the program with a TypeError: Cannot read properties of undefined.
- ?. (optional chaining) reads a property only when the value before it exists, and gives undefined instead of crashing.
- ?? gives a default only when the value is null or undefined. || gives the default for every falsy value, so it replaces a real 0 or "" too.

Jamal signed up for the Riverside 5K but skipped the emergency contact. Sam never picked a shirt size. Real data always has holes like these: a form field someone left blank, a user with no profile picture, a song with no album. The code that reads the data has to cope, or the page stops with one of the most common error messages in JavaScript: *Cannot read properties of undefined*. This lesson shows the two kinds of "nothing" JavaScript has, and two short operators that handle them: `?.` and `??`.

## undefined: nothing was ever put here

Think of a row of mailboxes in an apartment building. A box with no name on it was never given to anyone. That's **undefined**: JavaScript's value for "nothing has been put here yet". You get it without asking for it, in a few everyday ways:

```javascript
const sam = { name: "Sam Okafor", email: "sam.okafor@example.com" };

let bibNumber;
console.log(bibNumber);
console.log(sam.shirtSize);

function sayHello() {
  console.log("Hello!");
}
console.log(sayHello());
```

```text
undefined
undefined
Hello!
undefined
```

A variable declared with no value, a property the object doesn't have, and the result of a function with no `return`: all three are `undefined`.

## null: empty on purpose

Now picture a form that asks "Emergency contact?" and Jamal ticks **None for now**. That's a real answer: the box is empty because someone said so. JavaScript's value for that is **null**. You never get `null` by accident; a person or a program puts it there. (Some built-in methods you'll meet later give back `null` to mean "nothing found".)

```javascript
const jamal = {
  name: "Jamal Reed",
  email: "jamal.reed@example.com",
  emergencyContact: null,
};

console.log(jamal.emergencyContact);
console.log(jamal.emergencyContact === null);
console.log(null == undefined);
console.log(null === undefined);
```

```text
null
true
true
false
```

`null` and `undefined` are loosely equal (from the last lesson, `==` treats them as the same), but not strictly equal. Both are falsy, and together they're called **nullish** values. One old quirk to know: `typeof null` gives `"object"`, a mistake from JavaScript's first version that can't be fixed now without breaking old websites.

## The crash: reading a property of nothing

Maria gave an emergency contact, so `maria.emergencyContact.phone` is her contact's number. Try the same line on Jamal, whose contact is `null`, and the program stops:

```javascript
const jamal = { name: "Jamal Reed", emergencyContact: null };

console.log(jamal.emergencyContact.phone);
console.log("Check-in sheet printed.");
```

Node prints *TypeError: Cannot read properties of null (reading 'phone')*, and the second line never runs. `jamal.emergencyContact` is `null`, and `null` has no properties to read. With a property that doesn't exist at all, the message says *undefined* instead of *null*. When you see this error, look at the part **before** the last dot: that's the value that was missing.

## Read carefully with ?.

**Optional chaining**, `?.` (read "question mark dot"), is a careful dot. It looks at the value on its left first: if that value is `null` or `undefined`, it stops and gives back `undefined` instead of crashing. Otherwise it reads the property as a plain dot would:

```javascript
const maria = {
  name: "Maria Lopez",
  emergencyContact: { name: "Ana Lopez", phone: "555-555-0142" },
};
const jamal = { name: "Jamal Reed", emergencyContact: null };

console.log(maria.emergencyContact?.phone);
console.log(jamal.emergencyContact?.phone);
console.log(jamal.coach?.name);
```

```text
555-555-0142
undefined
undefined
```

Put `?.` only where something really might be missing. A missing runner name is a bug you want to hear about, so `runner.name` keeps its plain dot.

## A default with ??

A missing value often needs a stand-in. The Riverside 5K's rule is that a runner who doesn't pick a shirt gets a medium. **Nullish coalescing**, `??` (read "double question mark"), gives you that: `a ?? b` is `a`, unless `a` is `null` or `undefined`, and then it's `b`. (*Coalescing* means "coming together into one": two values in, one out.)

```javascript
const sam = { name: "Sam Okafor" };
const priya = { name: "Priya Shah", shirtSize: "S" };

console.log(sam.shirtSize ?? "M");
console.log(priya.shirtSize ?? "M");

const jamal = { name: "Jamal Reed", emergencyContact: null };
const contactPhone = jamal.emergencyContact?.phone ?? "No emergency contact yet";
console.log(contactPhone);
```

```text
M
S
No emergency contact yet
```

The last example is a pair you'll write often: `?.` reads safely, and `??` fills the gap.

**Try it.** Make a file named `contacts.js` with Maria, Jamal and Sam as objects in an array (give Sam no `emergencyContact` property at all). Loop over them with `for...of`, and print each runner's name and their contact's name, or *none* when there isn't one.

## ?? or ||?

Before `??` existed, people wrote defaults with `||` (read "or"). `a || b` gives back `a` if it's truthy, and `b` otherwise. That looks the same, until the value is 0 or empty text. The sign-up form asks for a donation, and says *leave it blank for the suggested $10*. Priya typed 0; Sam left it blank:

```javascript
const priyaDonation = 0;
let samDonation;

console.log(priyaDonation || 10);
console.log(priyaDonation ?? 10);
console.log(samDonation || 10);
console.log(samDonation ?? 10);
```

```text
10
0
10
10
```

`||` replaces every falsy value, so it would charge Priya $10 after she chose 0. `??` replaces only `null` and `undefined`, so her 0 stands. Pick by asking one question: **is 0, `""` or `false` a real answer here?** If it is, use `??`. If every falsy value should get the default (an empty nickname box falling back to the runner's name, say), `||` is the one.

![Two doormen in front of a door marked "default": the ?? doorman lets the default in only for null and undefined; the || doorman lets it in for every falsy value, including 0 and empty text](https://fsdtrainingmedia.blob.core.windows.net/public/images/2026/10/1b4c896a59af40c49293c36243bc6ab7.webp)

## Check yourself

2 questions to try on the lesson page.

## So what

Data from forms and from the internet is never complete. With `?.` to read carefully and `??` to fill the gaps, your code keeps going when a value is missing, and keeps the 0s and empty answers people really gave.

## Key terms

- [undefined](https://www.ask-codey.com/glossary/undefined)
- [null](https://www.ask-codey.com/glossary/null)
- [Optional Chaining](https://www.ask-codey.com/glossary/optional-chaining)
- [Nullish Coalescing](https://www.ask-codey.com/glossary/nullish-coalescing)

## Cheat sheets

- [Missing Values: null, undefined, ?. and ??](https://www.ask-codey.com/cheat-sheets/javascript-tricky-values-null-and-undefined-cheat-sheet)

## Related

- [Equality and Truthy Values](https://www.ask-codey.com/lessons/javascript-tricky-values-equality-and-truthiness)
- [What Is an Object](https://www.ask-codey.com/lessons/javascript-objects-what-is-an-object)

Source: https://www.ask-codey.com/lessons/javascript-tricky-values-null-and-undefined
