Logical and nullish assignment operators in JavaScript
Before we dive in, let’s first define truthy, falsy, and nullish values.
Falsy are: null, undefined, false, NaN, 0, -0, 0n a BigInt zero, "", document.all.
Truthy are: any other value than falsy is considered truthy.
Nullish are: undefined, null.
Why is document.all falsy? Follow the rabbit hole here.
Logical assignment operators are &&=, ||= and the nullish coalescing assignment is ??=.
Here is an overview of what they do:
x ||= yis equivalent tox || (x = y). Assigns y to x when x is falsy.x &&= yis equivalent tox && (x = y). Assigns y to x when x is truthy.x ??= yis equivalent tox ?? (x = y). Assigns y to x when x is nullish.
When to use what?
You don’t see these operators in the wild that much because they can be confusing at first. But there are cases when they’re useful! 💪 I tried to come up with rules that may be helpful when deciding whether to use this or not.
||=
Use it when updating values that can be falsy.
For undefined and null types there is no difference between ||= and ??=.
function createPost(title, body) {
title ||= 'Untitled'
body ||= 'Empty body'
// ...
}
// Which is the same as:
function createPost(title, body) {
if (title === '') {
title = 'Untitled'
}
if (body === '') {
body = 'Empty body'
}
// ...
}
&&=
Use it when updating truthy value. The snippet below should give you a rough idea about proper usage. You can update an already set value easily and skip the update when the value hasn’t been initialized yet.
const config = {
user: undefined, // this can be also { user: string, password: string, admin: boolean }
host: 'https://example.com',
}
// Will be executed only if a user is present
// Set user to admin
config.user &&= {
...config.user,
admin: true
}
fetch('https://example.com/api/resource', {
headers: { 'content-type': 'application/json' },
body: JSON.stringify(config)
})
??=
Use it when setting a new value on nullish or uninitialized variable. It is also useful for saving expensive computation. See snippet below:
class Fetcher {
users // undefined at first
// Fetching is an expensive operation, we can do it only once
// and then serve users from this.users variable.
async getUsers() {
this.users ??= await fetch('http://example.com/api/users')
.then(r => r.json())
return this.users
}
}
/** @param {{ location: string|undefined }} jobPost */
function createJobPost(jobPost) {
jobPost.location ??= 'remote'
return jobPost
}
createJobPost({}) // => { location: 'remote }
Conclusion
With these operators we can remove some if statements guarding whether value is defined or not.
I recommend using &&= and ??= for most of the time.
Nullish values are more common for default values. In that case there is ??= for you.
When you want to update a value only when it is defined the &&= operator will serve you well.
Check previous post about 👉 JavaScript ES6 and beyond.