JavaScript Forced Conversions (“Type Coercion”)
Sometimes, instead of checking the type using ‘typeof’, JavaScript programmers will instead force a conversion of the argument to the data type they need (especially if intuition might fail). This technique is an instance of type coercion and results in code that is more fault tolerant because it won’t exit with an exception if the data type of the argument provided is incorrect.
Once again, let’s see how we can change our ‘lowercaseCompare’ example using this idea:
// Unsafe: function lowercaseCompare(a, b) { return a.toLowerCase() == b.toLowerCase(); } // Safer: function lowercaseCompare(a, b) { a = a.toString(); b = b.toString(); return a.toLowerCase() == b.toLowerCase(); }
In the re-written version of the ‘lowercaseCompare’ function, we are “forcing” the ‘a’ and ‘b’ arguments to be converted to a string. This allows us to safely call the ‘toLowerCase’ method without a crash. Now, if the ‘lowercaseCompare’ function is called, we get the following results:
lowercaseCompare("abc", "abc") // true lowercaseCompare("abc", 10) // false lowercaseCompare("10", "10") // true lowercaseCompare("10", 10) // true
However, the astute observer will notice the new version of ‘lowercaseCompare’ is marked “safer” rather than “safe.”
Why?
toString is not the most correct way to force a conversion to a string. (It’s also not the fastest due to runtime method lookups, but imagine having to consider all these details while writing one line of code? This is how programming for the web used to be before JS++.)
One example is if we try to call ‘lowercaseCompare’ with a variable we forgot to initialize, it will crash again if we use ‘toString’. Let’s try it:
function lowercaseCompare(a, b) { a = a.toString(); b = b.toString(); return a.toLowerCase() == b.toLowerCase(); } var a, b; // uninitialized variables var result = lowercaseCompare(a, b); console.log(result); // Never executes
No, instead, the most correct way to perform type coercion to string would be like this:
// Finally safe: function lowercaseCompare(a, b) { a += ""; // correct type coercion b += ""; // correct type coercion return a.toLowerCase() == b.toLowerCase(); } var a, b; var result = lowercaseCompare(a, b); console.log(result);
There’s just one problem left with the correct code: it becomes unreadable. What would your code look like if you had to insert += “” everywhere that you wish to express the intent that you want string data?
JS++ | Types in JavaScript
In this chapter, we’re going to explore JavaScript programming styles and how developers worked with types in JavaScript (rather than JS++). This chapter will help you understand the next chapters which explain the JS++ type system in detail.
In this tutorial, we will be using the Google Chrome web browser. Click here to download Google Chrome if you don’t already have it.
In order to execute JavaScript code, we’ll be using the Chrome Developer Tools console. Open Chrome and hit the Ctrl + Shift + J key combination and choose the “Console” tab.
Copy and paste the following code into your console and press enter to execute it:
var message; message = "This is a test."; if (Math.random() > 0.5) { message = 123; } console.log(message);
Hit your “up arrow” and hit “enter” to evaluate the code more than once. Try evaluating the code a few times.
Notice how the data type in the above code changes from a string to a number. However, it only changes to a number if a randomly-generated number is greater than 0.5. Therefore, the data type of the variable ‘message’ can be different each time the script is executed. This was a major problem in JavaScript. For example, the following JavaScript code is unsafe:
function lowercaseCompare(a, b) { return a.toLowerCase() == b.toLowerCase(); }
The reason is because toLowerCase() is a method that’s only available to JavaScript strings. Let’s execute the following JavaScript code in the Chrome console:
function lowercaseCompare(a, b) { return a.toLowerCase() == b.toLowerCase(); } console.log("First message."); lowercaseCompare("10", 10); // Crashes with 'TypeError' console.log("Second message."); // Never executes.
Notice how the script will crash with a TypeError. The second message never gets logged. The key takeaway is that the code crashed because toLowerCase() is not a method available for numbers, but the function was called with a string (“10”) and a number (10). The number argument was not a valid argument for the ‘lowercaseCompare’ function. If you change the function call, you will observe that the program no longer crashes:
// Change this: // lowercaseCompare("10", 10); // Crashes with 'TypeError' // to: lowercaseCompare("10", "10");
Developers worked around these problems in JavaScript by checking the types first. This is the safer way to rewrite the above ‘lowercaseCompare’ function in JavaScript:
function lowercaseCompare(a, b) { if (typeof a != "string" || typeof b != "string") { return false; } return a.toLowerCase() == b.toLowerCase(); }
We check the types using ‘typeof’, and, if we receive invalid argument types, we return a default value. However, for larger programs, this can result in a lot of extra code and there may not always be an applicable default value.