Conversion, coercion, and casting

So far, we have learned how to tell the difference between various types and characteristics within JavaScript using detection. As we have seen, detection is useful when needing to provide alternative values or warnings in the case of unexpected or incompatible values. There is an additional mechanism for dealing with such values, however: we can convert them from the values we don't desire into the values we do desire.

In order to convert a value, we use a mechanism known as casting. Casting is the intentional and explicit derivation of one type from another type. In contrast to casting, there is also coercion. Coercion is the implicit and internal process of conversion employed by JavaScript when we use operators or language constructs that require specific types. An example of this would be when passing String values to a multiplication operator. The operator will naturally coerce its String operands to numbers so that it can attempt to multiply them:

'5' * '2'; // => 10 (Number)

The underlying mechanisms in both casting and coercion are identical. They are both mechanisms of conversion. But how we access these low-level behaviors is key. If we do so explicitly, clearly communicating our intent, then the readers of our code will have a far nicer time.

Consider the following code, which contains two different mechanisms for converting a String into a Number:

Number('123'); // => 123
+'123'; // => 123

Here, we are using two different techniques to force the conversion of a value from a String into a Number. The Number() constructor, when called as a function, will internally convert the passed value into a Number primitive. The unary + operator will do the same, although it is arguably less clear. Coercion is even less clear as it often appears to occur as a side effect of some other operation. Here are some examples of this:

1 + '123'; // => "1234"
[2] * [3]; // => 6
'22' / 2; // => 11

The + operator, when either operand is a string, will coerce the opposite operand to a string and then concatenate them both together. The * operator, when given arrays, will call toString() on them and then coerce the resulting String into Number, effectively meaning that [2] * [3] is equal to 2 * 3. Also, the division operator will coerce its operands to numbers before operating on them. All of these coercive behaviors are happening implicitly.

The line between coercion and casting is not set in stone. It is possible, for example, to explicitly and intentionally convert a type via a coercive side effect. Consider the expression someString * 1, which could be used to cast a string to a number, using coercion to do so. In our conversions, what's crucial is that we clearly communicate our intent.

Coercion, since it happens implicitly, can be the cause of many bugs and unexpected behaviors. To avoid this trap, we should always have a strong level of confidence in the types of our operands. Casting, however, is entirely intentional and can help create a more reliable code base. It's common, on the more public or exposed sides of your interfaces, to preemptively cast to the types you desire, just in case the types you've received are not correct.

Observe here how we are explicitly casting both haystack and needle values to the String type:

function countOccurrences(haystack, needle) {

haystack = String(haystack);
needle = String(needle);

let count = 0;

for (let i = 0; i < haystack.length; count++, i += needle.length) {
i = haystack.indexOf(needle, i);
if (i === -1) break;
}

return count;
}

countOccurrences('What apple is the best type of apple?', 'apple'); // => 2
countOccurrences('ABC ABC ABC', 'A'); // => 3

Since we're relying on the indexOf() method on the haystack string, it makes sense, depending on our desired level of defensiveness, to cast the haystack to a string so that we can ensure it has the method available. Casting needle to a string also encodes a higher level of certainty so that we, and fellow programmers, can feel at ease.

The defensive approach of preemptively casting values to protect against undesirable types is best when we're crafting reusable utilities, public-facing APIs, or any interfaces that'll be consumed in a way that reduces your confidence in the types you'll be receiving.

Dynamically typed languages such as JavaScript are seen by many as an invitation to chaos. Such people may be used to the comfort and certainty provided by strictly typed languages. In truth, if wielded fully and carefully, a dynamic language can allow our code to be more thoughtfully composed and more resilient to the changing needs of users. In the remainder of this section, we'll be discussing the conversion to individual types, including the explicit casting mechanisms we can utilize and the various coercive behaviors the language adopts internally. We'll begin by looking at Boolean conversion.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.218.69