Throughout this series of articles, we've been talking about type coercion, how it differs from type conversion, and how it performs in dynamically typed languages.
If you're just joining the series, check out the earlier articles to make sure you're up to speed with where we are right now:
Whereas the other article focused on weakly typed languages and data types at a high level, we're going to be looking at some specific examples of type coercion in a weakly typed language, and the pitfalls that we may experience without knowing how type coercion works and how it can backfire.
The Rules of Comparison
Arguably, one of the most common problems that occurs in weakly typed languages comes whenever we're making comparisons. Sure, there are other times that expecting a variable to be one type when it's really another can negatively affect us, but the most common problems occur whenever we're performing some type of comparison.
These comparisons can come in the form of equality operations, conditional operations, bitwise operations, or during
As mentioned in previous articles in this series, different languages have different ways in which they go about coercing data types so the examples that we're looking at in this article may differ a little bit in the work that you do.
==), the strict equality operator (
===), and when using values such as
Null and Undefined
null are two different types of values. As if that's not enough, it can get even more confusing when comparing the two values.
First, note the following:
- If you were to execute
typeof( undefined )in the console, then the result would be
- If you were to executed
typeof( null )in the console, then the result would be
Next, if you were to declare a variable without actually assigning it a value, and you were to evaluate its type, then you'd see
/** * Declare a variable 'name' but don't assign it a value. * Execute the result of 'typeof' on a console and you'll * be given 'undefined.' */ var name; typeof( name );
Next, let's say that we opt to initialize the variable with an
null value. If you were to evaluate the variable using
typeof you'd be given an
// First, we'll declare the variable var number; /** * At this point, if we were to evaluate the variable * using typeof, then we'd be given 'undefined.' */ // Now assign the variable a value of 'null' number = null; /** * And evaluate the variable. At this point, we'll * be returned the value of 'object'. */ typeof( number );
null is an
object where undefined is its own type -
With that said, now we can actually look at why comparisons can become difficult whenever we perform comparisons on values without explicitly knowing their type.
Throughout this section, we're going to take a look at the results of comparing values that are of different types, but see how they are evaluated against one another using both equality comparisons and strict equality comparisons.
Note that all of the examples that we're listing below should be able to be executed within a browser's console such as Firebug or Chrome's Developer Tools.
For starters, we'll begin with
// Returns true undefined == null; null == undefined; // Returns false undefined === null; null === undefined;
Notice that in the first case, the equality operator is returning a value of the comparison after performing type coercion. That is, the interpreter is making its best guess as to what we mean when performing this comparison.
In the second case, we're using the strict equality operator. In this case, no type coercion occurs. Instead, it's taking the values exactly as they are and comparing them.
Next, let's take a look at declaring a variable, not assigning it a value, and then running a comparison.
// Declare the variable, don't assign it a value var example; // When compared to undefined, returns true in both cases example == undefined; example === undefined // When compared to null, returns true or false example == null; // true example === null; // false // Assign a value to the variable example = null; // Now do a strict comparison example === null; // Returns true
As you can see, thing's start to get a little more complicated when we begin to declare and compare variables with or without values.
Once we start introducing strings, numbers, and boolean values it can get even more complicated. First, let's start with strings and numbers. We'll begin by declaring a variable with a string value of
42 and a number with the
42 and then we'll perform our comparisons.
var sNumber, iNumber; sNumber = '42'; iNumber = 42; // Equality comparisons yields true sNumber == iNumber; // Strict comparison yields false sNumber === iNumber;
Again, notice in the first case, the interpreter attempts to coerce the values from the variables and then compare them. In the first case, it works - we're comparing a string value of
42 to a number value of
42, but when we use the strict equality comparison and get
The second case is technically more accurate because the first value is a string and the second is a number. Comparing two values of different types should always yield false.
Though we've taken a look at this in a previous article, what about the case of numbers and booleans?
var iNumber, bBoolean; iNumber = 0; bBoolean = false; // Returns true iNumber == bBoolean; // Returns false iNumber === bBoolean; // Returns true iNumber = 1; bBoolean = true; iNumber == bBoolean; // Returns false iNumber === bBoolean;
Finally, let's look at an example that combines strings, numbers, and booleans.
var sExample, iExample, bExample; sExample = '1'; iExample = 1; bExample = true; // Returns true sExample == iExample; // Returns false sExample === iExample; // Returns true iExample == bExample; // Returns false iExample === bExample; // Returns true sExample == bExample; // Returns false sExample === bExample;
Note that these are basic comparisons; however, when done in the context of an
if/else if/else you see how it can disrupt the flow of control through the conditional.
Note that when performing logical operations such as
|| as well as bitwise operators such as
| that the coercion rules still apply. To that end, you want to make sure that when you're performing those operations, use values of exactly the same type to get the most accurate results.
Otherwise, coercion may result in a false positive or a false negative.
This completes our cursory, beginner's look at data types and type coercion in a dynamically typed languages. Ultimately, the rules of thumb are to always use strict equality operators and to make sure that the variables with which you're working are of the same type. If you're not sure, you can always explicitly convert them using strategies that we outlined earlier in the series.
Throughout this series, we've taken a look at how types vary and behave from strongly typed languages to weakly typed languages. We've looked at how casting and coercion differ, and we've looked some of the potential pitfalls that this may result in relying too much on the interpreter or the compiler when performing comparisons.
Finally, we took a look at some strategies for how to write more defensive code by making sure that we have the data type that we need, and how to use strict comparison operators to ensure that we get the results that we need.