0

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.

acostela
  • 2,597
  • 3
  • 33
  • 50
  • 1
    I believe the `<=` operator is trying to cast the array to a number, with varied results (depending on the content of the array). You can experiment further in the console by casting those arrays to numbers yourself using the `+` operator: type in `+[[2]]` and see what you get back... – nnnnnn Apr 10 '17 at 06:59

1 Answers1

7

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:

  1. Since a contains an object reference, convert that object to a primitive, preferring a number to a string if possible:

    • Arrays don't provide a way of converting to number, so we end up doing (effectively) a.toString()
    • a.toString() calls a.join(), which joins the entries together as strings with a comma in between
    • So we end up with "2".


    Now we have "2" < 10

  2. Since one of those is a number, the operator coerces the other one to number, giving us 2 < 10

  3. The result of 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:

  1. 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

  2. Since one of those is a number, the operator coerces the other one to number. "[object Object]" coerced to a number is NaN.

  3. 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.)

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • This will be a different question If you feel that I need to open a new one tell me. But to avoid this kind of things why JS does not have a <== operator? Like the "===" or the "!==" – acostela Apr 10 '17 at 07:06
  • @acostela: I don't think there's a satisfying answr to that question. :-) See [*Why doesn't JavaScript have strict greater/less than comparison operators?*](http://stackoverflow.com/questions/14533046/why-doesnt-javascript-have-strict-greater-less-than-comparison-operators) You could propose them, of course: https://github.com/tc39/proposals – T.J. Crowder Apr 10 '17 at 07:12
  • @Rajesh: Not a bad idea. I misunderstood what you'd done originally and rolled it back, but I've added it back now, with parallel phrasing to the first example for consistency. – T.J. Crowder Apr 10 '17 at 07:26
  • Thank you very much :) I still get some confussions with cohertion but now is much more clear for me – acostela Apr 10 '17 at 07:27
  • @acostela: On the strict relational comparison, one roadblock is probably simply that there's an issue finding operators for it. `<==` and `>==` are easy, but what do we do for the `<` and `>` operations? `<<` and `>>` are taken. So we'd have to go another way, like `<*` and `>*`, which introduces inconsistency. So I think the utility never quite reached the level of being worth the syntax. – T.J. Crowder Apr 10 '17 at 07:30
  • I don't see a need for `<==`. If you're doing a less than or greater than test and aren't sure of the types of the operands then you've probably got bigger problems than could be resolved neatly with that operator. But if you really want it you can always write a `strictLessThan()` function. – nnnnnn Apr 10 '17 at 08:07