Reputation: 2727
I'm working with JS and I don't understand certain behavior with arrays. I searched but I can't find a correct answer, so sorry if this is dupe or in relation with another question.
I have the following code using lodash.
return _.difference(self.list1, self.list2) <= 0;
That is returning an array and I'm comparing it directly with a number, just because I forgot the .length property. I saw that this is "working" unless it is not correct. So I started to do some tests with the JS console and I don't understand what is happening here.
[Object, Object, Object] <= 0 //returns false
[] <= 0 //returns true
[[]] <= 0 //returns true
[[[]]] <= 0 //returns true
[[2]] <= 0 // returns false
[[],[]] <= 0 //returns false
What is JS doing here? Thank you very much.
Upvotes: 0
Views: 119
Reputation: 1074048
The <=
operator will coerce its operands to try to make them comparable. The full details are in the specification's Abstract Relational Comparison operation, but basically:
When you compare an object reference with a primitive, it tries to convert the referenced object to a primitive. In this case, since the primitive is a number, it will try to get a primitive number from the object if the object supports that (arrays don't) and then fall back on getting a string. At which point it has a string and a number, so it coerces the string to number and compares them numerically.
When you convert an array to a string, it does the same thing join
does: Joins the string version of all the entries together separated by commas. Of course, if you have only one entry, you'll end up with just that entry (as a string).
Here's a simpler example:
var a = [2];
console.log(a < 10); // true
The steps there are:
Since a
contains an object reference, convert that object to a primitive, preferring a number to a string if possible:
a.toString()
a.toString()
calls a.join()
, which joins the entries together as strings with a comma in between"2"
.
Now we have "2" < 10
Since one of those is a number, the operator coerces the other one to number, giving us 2 < 10
2 < 10
is true
This strategy sometimes, even frequently, means the <=
operator ends up comparing NaN
to something. For instance, if a
were a non-array object:
var a = {};
console.log(a.toString()) // "[object Object]"
console.log(a < 10); // false
The steps would be:
Since a
contains an object reference, convert that object to a primitive, preferring a number to a string if possible:
Plain objects don't support conversion to number, so we end up doing (effectively) a.toString()
.
As you can see in the snippet above, that gives us "[object Object]"
Now we have "[object Object]" < 10
Since one of those is a number, the operator coerces the other one to number. "[object Object]"
coerced to a number is NaN
.
The result of NaN < 10
is false
, because all comparisons involving NaN
(including ===
) result in false
. (Yes, really; NaN === NaN
is false
.)
(Thanks Rajesh for suggesting the {} < 10
example and providing the initial version of the above.)
Upvotes: 7