The KIRUPA orange logo! A stylized orange made to look like a glass of orange juice! Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Table of Contents

Booleans and the Stricter === and !== Operators

by kirupa   |   filed under JavaScript 101

While it's polite to say that all types are interesting and fun to be around, you and I both know that is a lie. Some types are just boring. The boolean type is one such example. Here is the reason why. Whenever we initialize a variable using either true or false, we create a boolean:

let sunny = false;
let traffic = true;

Congratulations. If you just know this, you are 80% of the way there in fully understanding how booleans operate. Of course, 80% isn't really adequate when you think about it. It's like eating a hot dog without any condiments. It's like watching a live concert and leaving before the encore set. It's like leaving a sentence mid.

What we are going to expand upon a bit here is the other 20% made up of various boolean quirks, the Boolean object, the Boolean function, and the important === and !== operators.

Onwards!

The Boolean Object

Booleans are meant to be used as primitives. I'm going to be extra lazy and just reuse the example you saw a few moments earlier to show you an example of what a boolean primitive would look like:

let sunny = false;
let traffic = true;

Like you've seen so many times already, behind every primitive there is an Object based representation lurking in the shadows. The way you create a new boolean Object is by using the new keyword, the Boolean constructor name, and an initial value:

let boolObject = new Boolean(false);
let anotherBool = new Boolean(true);

The initial value you can pass in to the Boolean constructor is commonly true and false, but you can pretty much pass anything in there that will result in the final evaluation being true or false. I will detail what kinds of values will predictably result in a true or false outcome in a little bit, but here is the obligatory warning from the Surgeon General about this approach: Unless you really REALLY want a Boolean object, you should stick with primitives.

The Boolean Function

There is one major advantage the Boolean constructor provides, and that advantage revolves around being able to pass in any arbitrary value or expression as part of creating your Boolean object:

let boolObject = new Boolean(< arbitrary expression >);

This is really advantageous because you may find yourself wanting to evaluate a boolean expression where the data you end up with isn't a clean true or a false. This is especially common when you are dealing with external data or code, and you have no control over which of the various false-y or true-y values you get. Here is a contrived example:

let isMovieAvailable = getMovieData()[4];

The value for isMovieAvailable is probably a true or false. When it comes to processing data, you often have no guarantee that something at some point will break or change what gets returned. Just like in real life, simply hoping that things will work is never adequate without you taking some actionable steps. The Boolean function is one such step.

Now, creating your own function to deal with the ambiguity may be overkill, but the downside with the Boolean constructor is that you are obviously left with a boolean object - which isn't desirable. Fortunately, there is a way to get the flexibility of the Boolean constructor with the lightweightness of a boolean primitive extremely easily. That way is led by the Boolean function:

let bool = Boolean(true);

The Boolean function allows you to pass in arbitrary values and expressions while still returning a primitive boolean value of true or false. The main difference in how you use it compared to the constructor approach is that you don't have the new keyword. W00t! Anyway, let's take a few moments and look at the variety of things you can pass in to the Boolean function, and note that all of this will also apply to what you can pass in to the Boolean constructor you saw in the previous section as well.

The values you can pass in to return false are null, undefined, empty/nothing, 0, an empty string, and (of course) false:

let bool;

bool = Boolean(null);
bool = Boolean(undefined);
bool = Boolean();
bool = Boolean(0);
bool = Boolean("");
bool = Boolean(false);

In all of these examples, the bool variable will return false. To return true, we can pass in a value of true or ANYTHING that results in something other than the various false values we saw earlier:

let bool;
        
bool = Boolean(true);
bool = Boolean("hello");
bool = Boolean(new Boolean()); // Inception!!!
bool = Boolean("false"); // "false" is a string
bool = Boolean({});
bool = Boolean(3.14);
bool = Boolean(["a", "b", "c"]);

In these examples, the bool variable will return a true. That may seem bizarre given some of the statements, so let's look at a few of the subtle things in play here. If what we are evaluating is an object, such as new Boolean(new Boolean()) the evaluation will always be true. The reason is that the mere existence of an object will trigger the true switch, and calling new Boolean() results in a new object. Extending this logic a bit, it means the following if statement actually results in a true as well:

let boolObject = new Boolean(false);

if (boolObject) {
    alert("Bool, you so crazy!!!");
}

It doesn't matter that the object we are evaluating is secretly a false in disguise...or a String object or an Array and so on. The rules for primitives are more simple. If we are passing in a primitive (or something that evaluates to a primitive), anything other than null, undefined, 0, an empty string, NaN, or false will result in a result of true.

Strict Equality and Inequality Operators

The last thing we are going to look at is going to combine what we know about types and booleans to add a twist to the various conditional operators we saw earlier. So, we know about == and != and have probably seen them in use a few times. These are the equality and inequality operators that let us know if two things are either equal or unequal. Here is the plot twist. There is a subtle and deviant behavior they exhibit that we may not be aware of.

Here is an example:

function theSolution(answer) {
    if (answer == 42) {
        alert("You have nothing more to learn!");
    }
}

theSolution("42"); //42 is passed in as a string

In this example, the expression answer == 42 will evaluate to true. This works despite the 42 us passing in being a string and the 42 we are checking against being a number. What is going on here? In what kind of a world is a string and a number equal? With the == and != operators, this is expected behavior. The value for the two things you are comparing is 42. To make this work, JavaScript forces the two different yet similar values to be the same under the hood. This is formally known as type coercion.

The problem is that this behavior can be undesirable - especially when this is happening without us knowing about it. To avoid situations like this, we have stricter versions of the equality and inequality operators, and they are === and !== respectively. What these operators do is that they check for both value and type and do not perform any type coercion. They basically force us to write code where the burden on ensuring true equality or inequality falls squarely on us. That is a good thing.

Let's fix our earlier example by replacing the == operator with the === operator:

function theSolution(answer) {
    if (answer === 42) {
        alert("You have nothing more to learn!");
    }
}

theSolution("42"); //42 is passed in as a string

This time around, the conditional expression will evaluate to false. In this stricter world, a string and number are of different types despite the values being similar. Because no type coercion takes place, the final result is false.

The general word on the street is to always use the stricter forms of the equality and inequality operators. If anything, using them will help us to spot errors in our code - errors that might otherwise turn out very difficult to identify.

Different Objects that Look the Same...are Still Different!

If we are comparing two different objects, the strict equality operator (and the not-so-strict equality operator) won’t work as we might expect. For example, all of these cases below will be false:

console.log(new String("A") == new String("A"));
console.log([1, 2, 3] == [1, 2, 3]);
console.log({ a: 1 } == { a: 1 });

Keep that in mind when comparing the equality or inequality of two separate, individual objects.

Conclusion

Booleans make up one of the most frequently used types in our code. They play a key role in allowing our code branch out into different directions despite the simplicity they exhibit on the surface. While I can count on one hand the number of times I had to use the Boolean function or even the stricter equality and inequality operators, there aren't enough hands with fingers for me to count the number of times I've encountered these strange things in the wild.

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends