The code:
var num = 20;
if(num == "20")
{
alert("It works");
}
else
{
alert("Not working");
}
The question:
In C programming we have a rule name data type promotion, where when there's a mix of data type (example: addition of integer and floating point), the integer will first converted to floating point before the addition is being carry out.
The code above will prompt me an alert box with the message "It works"
that shows the if
test condition is evaluate to true.
For loosely typed JavaScript, I'm just curious: is there any rule like C that determines which conversion will be carry out in which situation? Besides that, the JavaScript code above converts num
variable value from an integer value to string value before making comparison or vice versa?
Yes, all the rules of type conversion applied by the equals operator are described on the ECMA-262 specification, in The Abstract Equality Comparison Algorithm.
The algorithm might look quite complex but it can be summarized to the following cases:
The type the two operands is the same:
- For primitives (String, Number, Boolean, Null, Undefined)
- Return true if the value is exactly the same
- For the Object type
- Return true if the two references point to the same object
If the types of the two operands differ
- If the type of one operand is either Null or Undefined
- Return true only if the other operand value is either
null
or undefined
- If one of the operands is of type Boolean or Number
- (after some steps) Convert the other operand to Number and compare
If one of the operands is an Object and the other is a primitive
- Perform Object-to-Primitive conversion on the Object and compare again
The Object-to-Primitive conversion is made through an abstract operation called ToPrimitive
, this method will try to convert the object to a primitive value, using the internal [[PrimitiveValue]]
method.
This will try to ejecute the object's valueOf
and toString
methods, and it will take the value of the first that returns a primitive value.
In the case those two methods don't return a primitive, or they aren't callable, a TypeError
is thrown, e.g.:
1 == { toString:null } // TypeError!
The above statement will produce a TypeError
because the default Object.prototype.valueOf
method doesn't do anything more than actually the same object instance (this
, not a primitive value) and we are setting an own toString
property that's not a function.
A friend made small tool that might be interesting to you, it shows all the steps and recursive comparisons made between types:
In JavaScript, there are two operators that can be used to compare two values: the ==
and ===
operators.
Quoted from JavaScript The Definitive Guide 6th Edition:
The equality operator ==
is like the strict equality operator (===
), but it
is less strict. If the values of the two operands are not the same type,
it attempts some type conversions and tries the comparison again.
And
The strict equality operator ===
evaluates its operands, and then compares the
two values as follows, performing no type conversion.
So I suggest that you use ===
all the time to avoid problems like:
null == undefined // These two values are treated as equal.
"0" == 0 // String converts to a number before comparing.
0 == false // Boolean converts to number before comparing.
"0" == false // Both operands convert to numbers before comparing.
P.S. I could post the entire "comparison guidelines" as written in the book but it's too long ;) Just tell me and I'll edit my post for you.
Avoid implicit type conversion in JavaScript. Always take steps to test and/or convert individual values before comparing them to ensure you are comparing apples to apples. Always test explicitly for undefined to determine if a value or property has a value, use null to indicate that object variables or properties do not refer to any object, and convert & compare all other values to ensure operations are performed against values of the same type.
I know the question has been answered. What I have given below is an example of few conversions. It will be useful for someone who is new to JavaScript. The below output can be compared with the general algorithm for an easy understanding.
The code:
var values = ["123",
undefined,
"not a number",
"123.45",
"1234 error",
"",
" ",
null,
undefined,
true,
false,
"true",
"false"
];
for (var i = 0; i < values.length; i++){
var x = values[i];
console.log("Start");
console.log(x);
console.log(" Number(x) = " + Number(x));
console.log(" parseInt(x, 10) = " + parseInt(x, 10));
console.log(" parseFloat(x) = " + parseFloat(x));
console.log(" +x = " + +x);
console.log(" !!x = " + !!x);
console.log("End");
}
The output:
"Start"
"123"
" Number(x) = 123"
" parseInt(x, 10) = 123"
" parseFloat(x) = 123"
" +x = 123"
" !!x = true"
"End"
"Start"
undefined
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = false"
"End"
"Start"
"not a number"
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = true"
"End"
"Start"
"123.45"
" Number(x) = 123.45"
" parseInt(x, 10) = 123"
" parseFloat(x) = 123.45"
" +x = 123.45"
" !!x = true"
"End"
"Start"
"1234 error"
" Number(x) = NaN"
" parseInt(x, 10) = 1234"
" parseFloat(x) = 1234"
" +x = NaN"
" !!x = true"
"End"
"Start"
""
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = false"
"End"
"Start"
" "
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = true"
"End"
"Start"
null
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = false"
"End"
"Start"
undefined
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = false"
"End"
"Start"
true
" Number(x) = 1"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 1"
" !!x = true"
"End"
"Start"
false
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = false"
"End"
"Start"
"true"
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = true"
"End"
"Start"
"false"
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = true"
"End"
Better use below code for understanding implicit conversion.
var values = [ 0 , 123, "0", "123", -0, +0, NaN, +NaN, -NaN, false, true, "false", "true", null, undefined, "null", "undefined", "", "GoodString", " "];
for (var i = 0; i < values.length; i++){
console.log("<<<<<<<<<<<<Starting comparing: " + i + ">>>>>>>>>>>>>>>");
for (var j = 0; j < values.length; j++){
console.log(values[i],`==`, values[j]);
console.log(eval(values[i] == values[j]));
}
}