Coercion in JavaScript

Coercion in JavaScript

What is Coercion in JavaScript ?

Answer : Coercion in JavaScript refers to the automatic or implicit type conversion of values from one data type to another.

For example, JavaScript might convert a string to a number or vice versa depending on the context in which it is used.

Why Coercion ?

Coercion happens in JavaScript to make operations between different data types possible without explicitly converting them yourself. JavaScript tries to automatically convert values to a common type that makes the operation meaningful.

If you want to understand more about coercion , so you can prefer Mozilla Docs for coercion - Type Coercion

How does Coercion Work

Coercion works through two main types: implicit and explicit. Implicit coercion happens automatically, while explicit coercion happens when you intentionally convert a value to a different type. Let's understand with example.

```JavaScript Code below

console.log(2 + '3'); // Outputs '23' - implicit coercion converts number to string.```

In above code , "Why did I only convert the number 2 to a string? Why didn't I convert ' 3 ' from a string to a number for the addition?"

To understand this , there is a step of algorithm for addition operator ' + ' which perform implicit to conversion.

  1. Let lref be the result of evaluating AdditiveExpression.

  2. Let lval be ? GetValue(lref).

  3. Let rref be the result of evaluating MultiplicativeExpression.

  4. Let rval be ? GetValue(rref).

  5. Let lprim be ? ToPrimitive(lval).

  6. Let rprim be ? ToPrimitive(rval).

  7. If Type(lprim) is String or Type(rprim) is String, then

    1. Let lstr be ? ToString(lprim).

    2. Let rstr be ? ToString(rprim).

    3. Return the string-concatenation of lstr and rstr.

  8. Let lnum be ? ToNumber(lprim).

  9. Let rnum be ? ToNumber(rprim).

The above algorithm seems very difficult to understand. But the reality is that it perform very simple.

Let's Understand this , here 'lref' represent the left operand of an expression , 'lval' is a variable which store the value of 'lref' fetched by 'getValue' value, similarly for right operand we do. After that we apply ToPrimitive(lval) for left operand which is abstract operation , don't worry we will see also abstract operation. And the return value stores in 'lprism' similiar for 'rval' . After all this we check the type of 'lprism' and 'rprism' depends on that , perform abstract operation on them like 'ToNumber' , 'ToString' etc.

Abstract Operation

Abstract operation are those operation or set of algorithms which are not a part of Official 'EcmaScript' Docs but it present to help the developers for understanding in docs . And we can not access directly , it is only implemented by JavaScript functionality.

Operation which mostly useful For Type Conversion

  1. ToNumber(argument)

  2. ToPrimitive(input,[Preferred Type])

  3. ToBoolean(argument)

  4. ToString(argument)

1 . ToNumber(argument)

The abstract operation 'ToNumber' converts argument to a value of type Number according to below table.

Argument TypeResult
UndefinedReturn NaN.
NullReturn +0.
BooleanIf argument is true, return 1. If argument is false, return +0.
NumberReturn argument (no conversion).
StringSee grammar and conversion algorithm below.
SymbolThrow a TypeError exception.
Object1 . Let primValue be ? ToPrimitive(argument ,hintNumber). 2 . Return ? ToNumber(primValue).

With the help of this table we can convert any valid type of JavaScript into Number. If you want to understand more prefer Official Docs. 'ToNumber'.

If you want to understand with code , so you can also check my github repo . Coercion

2 .ToPrimitive(Input , [Preferred Type])

The abstract operation 'ToPrimitive' takes an input argument and an optional argument PreferredType. The abstract operation ToPrimitive converts its input argument to a non-Object type. If an object is capable of converting to more than one primitive type, it may use the optional hint PreferredType to favour that type. Conversion occurs according to the following algorithm:

  1. Assert: input is an ECMAScript language value.

  2. If Type(input) is Object, then

    1. If PreferredType is not present, let hint be "default".

    2. Else if PreferredType is hint String, let hint be "string".

    3. Else PreferredType is hint Number, let hint be "number".

    4. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).

    5. If exoticToPrim is not undefined, then

      1. Let result be ? Call(exoticToPrim, input, « hint »).

      2. If Type(result) is not Object, return result.

      3. Throw a TypeError exception.

    6. If hint is "default", set hint to "number".

    7. Return ? OrdinaryToPrimitive(input, hint).

  3. Return input.

With help of this algorithm we mostly convert object type into another type.

For code understanding , prefer my repository Coercion

2.1 OrdinaryToPrimitive ( O, hint )

When the abstract operation OrdinaryToPrimitive is called with arguments O and hint, the following steps are taken:

  1. Assert: Type(O) is Object.

  2. Assert: Type(hint) is String and its value is either "string" or "number".

  3. If hint is "string", then

    1. Let methodNames be « "toString", "valueOf" ».
  4. Else,

    1. Let methodNames be « "valueOf", "toString" ».
  5. For each name in methodNames in List order, do

    1. Let method be ? Get(O, name).

    2. If IsCallable(method) is true, then

      1. Let result be ? Call(method, O).

      2. If Type(result) is not Object, return result.

  6. Throw a TypeError exception.

3 . ToBoolean ( argument )

The abstract operation ToBoolean converts argument to a value of type Boolean according to Table:

Argument TypeResult
UndefinedReturn false.
NullReturn false.
BooleanReturn argument.
NumberIf argument is +0, -0, or NaN, return false; otherwise return true.
StringIf argument is the empty String (its length is zero), return false; otherwise return true.
SymbolReturn true.
ObjectReturn true.

In ToBoolean() , undefined , null , NaN , +0 , -0 , " " as empty string are the falsy value means whenever we apply ToBoolean for these data type , it return false and the remaining standard data types return 'true'.

Let's see example:

var value = "";         
if(value) console.log("False");
else console.log("True");  
 /*output will be True because empty string return false and
 if condition would be false then else would be execute.*/

Abstract Equality Comparison

Abstract equality is the double equal comparison like x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

    1. If Type(x) is the same as Type(y), then

      a. Return the result of performing Strict Equality Comparison x ===y.

      1. If x is null and y is undefined, return true.

      2. If x is undefined and y is null, return true.

      3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).

      4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.

      5. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.

      6. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).

      7. If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).

      8. If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.

      9. Return false.

Strictly Equality Comparison

Strictly equality is a triple equal to comparison like x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. If Type(x) is different from Type(y), return false.

  2. If Type(x) is Number, then

    1. If x is NaN, return false.

    2. If y is NaN, return false.

    3. If x is the same Number value as y, return true.

    4. If x is +0 and y is -0, return true.

    5. If x is -0 and y is +0, return true.

    6. Return false.

  3. Return SameValueNonNumber(x, y).

Now , the question is like what's the difference between abstract equality and strictly equality?

Both equality checks type but after that abstract equality does coercion and strictly equality does not coercion .

Now after all the stuff , let's see corner cases of coercion :-

// ToString
console.log("" + 0);
console.log("" - 0);
console.log("" + (-0));
console.log("" - (-0));
console.log("" + []);
console.log("" - []);
console.log("" + {});
console.log("" - {});
console.log("" + [1,2,3]);
console.log("" + [null , undefined]);

// ToNumber
console.log(0-"010");
console.log(0-"o10");
console.log(0 - '010');

console.log(0-"0xF");
console.log(0-"0xa");
console.log(0-"0xb");
console.log(0-"0xc");
console.log(0-"0xd");
console.log(0-[]);
console.log(0-0xd);

Do this above code as practice and try to understand all concept whatever we seen above.