DEV Community

Samantha Ming
Samantha Ming

Posted on

3 Ways to Set Default Value in JavaScript

Code Tidbit by SamanthaMing.com

My go-to has always been the ternary operator for assigning a value to a variable conditionally. But ever since I discovered that โ€œ||โ€ can be used as a selector operator, Iโ€™ve been using that more. I find my code so much easier to read ๐Ÿ‘

Yes, it takes some time to wrap your head around it. But once you grasp the concept, itโ€™s super handy. Now I donโ€™t think less code makes your code better. But in this instance, I prefer the || operator ๐Ÿคฉ

let isHappyHour = '๐Ÿบ';

// Logical Operator
isHappyHour = isHappyHour || '๐Ÿต'; // '๐Ÿบ'

// Ternary
isHappyHour = isHappyHour ? isHappyHour : '๐Ÿต'; // '๐Ÿบ'

// If/Else
if (isHappyHour) { 
  isHappyHour = isHappyHour 
} else { 
  isHappyHour = '๐Ÿต' 
}

console.log(isHappyHour); // '๐Ÿบ'

Understanding the || Operator

I'm sure most of you thought the || is only used for boolean checks like this:

if(a || b) {
  // do something
}

BUT! You can also use it to evaluate the selected expression and produce a value. And that's why it's helpful to think of the logical operator as also a selector operator. When it's used with non-boolean values, the || operator will return a non-boolean value of one of the specified expression or operands.

Head explosion yet?! No worries, let me explain it the way Kyle Simpson explains it. He is the author of "You Don't Know JavaScript" - a free JavaScript e-book.

The value produced by a && or || operator is not necessarily of type Boolean. The value produced will always be the value of one of the two operand expressions.

Alright, let's look at an example.

const truthy = true;
const falsy = false;
const poop = '๐Ÿ’ฉ';

truthy || poop; // true
falsy || poop; // '๐Ÿ’ฉ';

As long as the 1st expression (left side) is truthy, it will always be the one selected. However, if the 1st expression (left side) is ever falsy, then the 2nd expression (right side) will be by default output. And that's why this || is known as the operator to set default values.

Using Default Value as Function Parameter

Quite often you would see || being used like this:

function(name) {
  name = name || 'no name';
}

Note: this is not the recommended way anymore. It's way better to ES6's default parameters. Because quite often, you might not want the default to kick in for ALL falsy values -- I'll explain falsy values in the next section. Most likely, we only want the default value to be set if no value or undefined is passed as the argument.

The better solution with ES6 Default Parameters

function(name = 'no name') {
}

Falsy Values

In the || operator, the 2nd expression (right side) is only evaluated IF the 1st expression (left side). So let's check what constitutes a falsy value.

// JS Essentials: Falsy Values

false
undefined
null
NaN
0
"" or '' or `` (empty string)

(I wrote another blog post on Falsy Values, which you can read it here)

Compared to the && operator

In my previous post, I wrote about the && operator. (Read it here). The && is also known as the Guard Operator. So here's a quick summary of the difference:

  • ||: 1st expression is always outputted. The 2nd expression only gets outputted if the 1st expression is falsy.

  • &&: 1st expression is outputted if it's FALSY. The 2nd expression only get outputted if the 1st expression is truthy.

What is the Elvis Operator

This is an interesting one. In JavaScript we have || to set default values. In other languages such as C++, this behavior is similar to the Elvis Operator which is denoted as ?:.

// JavaScript
someVariable || 'default value'

// Elvis Operator (not JavaScript)
someVariable ?: 'default value'

As to why it's called the Elvis Operator:

Credit to GlobalNerdy.com

Image Credit to GlobalNerdy.com

When to use which?

Now that you understand Falsy Values, let's figure out which way of the 3 ways is better to use.

๐Ÿ†Logical Operator ||

This is great if you want to capture all falsy values. It's less code and it's way easier to read. Assuming that everyone understands all 3 behaviors, of course.

NOTE: I'm not saying less code is always better, one can easily try to be too clever and shorten the code which renders it unreadable. We write code for others, it's a form of communication. It's always better to go with the option that conveys understanding over being clever.

let a;

// โœ… Short and simple
a = a || b;

// โ˜น๏ธ Meh, could be better
a = a ? a : b;

// ๐Ÿ˜ฑ Ouch
if (a) {
  a = a;
} else {
  a = b;
}

๐Ÿ†Ternary Operator

Let's say we don't want to capture ALL falsy values. And we only want the default value to kick in when it's undefined

// โŒ Logical doesn't work
a = (a === undefined) || b;
// (a === undefined) > will output a boolean 'true' not the actual value

// โœ… Ternary works
a = (a === undefined) ? a : b;

// โ˜น๏ธ Of course if/else will also work...but Ouch
if (a === undefined) {
  a = a;
} else {
  a = b;
}

๐Ÿ†If/Else

This is the option with the MOST control. And it's something I would absolutely go for if say, I need to perform an additional action.

// โœ… If/Else is much better
if (a) {
  a = a;
  // do something else
} else {
  a = b;
}

Resources


Thanks for reading โค
Say Hello! Instagram | Twitter | Facebook | Medium | Blog

Top comments (6)

Collapse
 
mustafaabobakr profile image
Mostafa Abobakr

We can make a helper function

export function defaultValue(VAL, DEFAULT) {
  switch (VAL) {
    case 'null':
    case 'undefined':
    case null:
    case undefined:
      return DEFAULT;
    default:
      return VAL;
  }
}

const a = defaultValue( $(el).val() , 'ok');  // a = 'ok' .... if   $(el).val() === to any case  ๐Ÿ‘†
Collapse
 
fluffynuts profile image
Davyd McColl

Just beware of when you have an original value which is falsey, for example, I recently had to get a numeric value from either the body of a request or the query string, using swagger. Swagger, since it was informed that the value would be numeric, had already populated the model with a numeric value.

So I had code (kinda) like this:

return getValueFromBody() || getValueFromQuery();

The problem was that the value in the swagger body object was zero, so it was falling over to attempt to get the value from the query, where it didn't exist, so the final result was undefined! This caused me a bit of a puzzlement, and I had to rather use:

var fromBody = getValueFromBody();
return fromBody === undefined
    ? getValueFromQuery()
    : fromBody;

So if you're using ||, just watch out for valid falsey values: if you can use empty string, zero or an actual false as a value, don't use || (:

Collapse
 
samanthaming profile image
Samantha Ming

Yes! โ€œ||โ€ will capture ALL falsy values listed in the article. Definitely something to be really mindful of. Thanks for sharing your example and emphasizing that point! Very important and a very common gotcha that can cause extreme headaches ๐Ÿ˜ต

Collapse
 
moopet profile image
Ben Sinclair

Given that your "ouchy" if/elses are only checking for truthiness, you'd miss out the a = a; part in the real world, though.

// โœ… If/Else is much better
if (a) {
  a = a;
  // do something else
} else {
  a = b;
}

becomes just this:

if (!a) {
  a = b;
}

and copes with specific tests like:

if (a === undefined) {
  a = b;
}

and that doesn't seem long or awkward to me; it seems explicit and readable.
Personally, I like the Elvis style (though it looks more like Jonny Bravo to me...) or the null coalescing operator you have in other languages (like SQL and nowadays even PHP), which has the benefit of being chainable like the || method.

Collapse
 
jakebman profile image
jakebman • Edited
// โœ… Ternary works # Ternary does not work.
a = (a === undefined) ? a : b;

The code in this segment is backwards - you're setting a to a only if it is undefined, and using b in all other cases.

Collapse
 
tombarr profile image
Thomas Barrasso

Nice article, I would love to see the Elvis operator in Javascript!

Another way you can assign default values is with Proxy. This can be done explicitly, implicitly, on a per-type basis, etc.

const withZeroValue = (target, zeroValue = 0) => new Proxy(target, {
  get: (obj, prop) => (prop in obj) ? obj[prop] : zeroValue
})

let position = withZeroValue({
  x: 10,
  y: 15
})

position.z // 0