Coercing objects to primitives
This blog post looks at how JavaScript coerces objects to primitives. If you don’t know the difference between primitive values and objects, I suggest you consult my article “Categorizing values in JavaScript” at the Adobe Developer Connection.
This post was triggered by the following tweet by David Bruant:
The result of the above expression is true. Let us first learn about coercion in JavaScript. We can then use that knowledge to understand this result.
Many operators and functions in JavaScript expect their arguments to have certain types. If they don’t, they are coerced (converted) to those types.
Coercing an object to a primitive type is a two-step process: First, the object is converted to a primitive. Then, if necessary, the primitive is converted to the correct type. Two methods are used to convert an object to a primitive:
There are three conversion algorithms:
The number algorithm first calls valueOf() and uses the returned value if it is primitive. Otherwise, it calls toString() and uses its value if it is primitive. Otherwise, an exception is thrown. The string algorithm calls the methods in reverse order. The default algorithm is “number” for non-dates and “string” for dates.
There are two common ways for coercing to number: the unary plus operator and Number, used as a function (not as a constructor).
In both cases, things work as expected: the number algorithm is used. Then the result returned by valueOf() is converted to number.
Two common ways of coercing a value to string are: the binary plus operator where one operand is a string and String, used as a function (not as a constructor).
The binary plus operator uses the default algorithm, because one can add either numbers or strings.
Two ways of coercing to boolean are: using the unary negation operator twice (once converts to boolean and negates) or using Boolean as a function.
Here we see that objects are never converted to primitive. The rule is simply: any object is always true. For primitives, only the following values are coerced to false, all other values are coerced to true.
Now it should be obvious why !!(new Boolean(false)) evaluates to true: Any instance of Boolean is always an object and those are always coerced to true.
Here are a few recommendations for coercion and objects:
This post was triggered by the following tweet by David Bruant:
!!(new Boolean(false)) #wtfjs
The result of the above expression is true. Let us first learn about coercion in JavaScript. We can then use that knowledge to understand this result.
Coercion
Many operators and functions in JavaScript expect their arguments to have certain types. If they don’t, they are coerced (converted) to those types.
Coercing an object to a primitive type is a two-step process: First, the object is converted to a primitive. Then, if necessary, the primitive is converted to the correct type. Two methods are used to convert an object to a primitive:
- valueOf()
- toString()
There are three conversion algorithms:
- “Number”: you expect the value to be a number.
- “String”: you expect the value to be a string.
- “Default”: you don’t have any expectations for the value.
The number algorithm first calls valueOf() and uses the returned value if it is primitive. Otherwise, it calls toString() and uses its value if it is primitive. Otherwise, an exception is thrown. The string algorithm calls the methods in reverse order. The default algorithm is “number” for non-dates and “string” for dates.
Let’s try out coercion via the following object:
var obj = {
valueOf: function () {
console.log("valueOf");
return '0';
},
toString: function () {
console.log("toString");
return 1;
}
};
Coercing to number
There are two common ways for coercing to number: the unary plus operator and Number, used as a function (not as a constructor).
> +obj
valueOf
0
> Number(obj)
valueOf
0
In both cases, things work as expected: the number algorithm is used. Then the result returned by valueOf() is converted to number.
Coercing to string
Two common ways of coercing a value to string are: the binary plus operator where one operand is a string and String, used as a function (not as a constructor).
> ''+obj
valueOf
'0'
> String(obj)
toString
'1'
The binary plus operator uses the default algorithm, because one can add either numbers or strings.
Coercing to boolean
Two ways of coercing to boolean are: using the unary negation operator twice (once converts to boolean and negates) or using Boolean as a function.
> !!obj
true
> Boolean(obj)
true
Here we see that objects are never converted to primitive. The rule is simply: any object is always true. For primitives, only the following values are coerced to false, all other values are coerced to true.
- undefined
- null
- false
- +0, -0, NaN
- ""
Understanding the initial result
Now it should be obvious why !!(new Boolean(false)) evaluates to true: Any instance of Boolean is always an object and those are always coerced to true.
Recommendations
Here are a few recommendations for coercion and objects:
- Stay away from instances of Boolean, Number and String. You don’t normally need or encounter them in JavaScript.
- However, I do like using Boolean, Number and String as functions, to coerce values. They are nicely descriptive when used in this manner.
- Obviously, all of the above ways of coercing to primitives work for any value, not just for objects:
> Number("123")
123
> Boolean(0)
false
> String(true)
'true'
- One does not often coerce objects to primitives. Doing so is, however, good for many WTFs [1] and hacks [2].
Further reading
- What is {} + {} in JavaScript? [Describes the binary plus operator and the conversion to number and string in detail]
- Fake operator overloading in JavaScript [a fun hack involving objects being coerced to numbers]
- JavaScript’s two zeros
Comments
Post a Comment