Eph
Eph

Reputation: 1321

Why is function <= function true, but NaN <= NaN false in ECMAscript

I am trying to understand the mystery of comparing functions:

let a=function(){}
let b=function(){}
console.log(a==b)  //false
console.log(a===b) //false
console.log(a<b)   //false
console.log(a>b)   //false
console.log(a<=b)  //true !?!
console.log(a>=b)  //true !?!

console.log( (+a) <  (+b) )  //false
console.log( (+a) >  (+b) )  //false
console.log( (+a) <= (+b) )  //false
console.log( (+a) >= (+b) )  //false

In ECMAscript the <= operator is described:

RelationalExpression:RelationalExpression<=ShiftExpression

  1. Let lref be the result of evaluating RelationalExpression.
  2. Let lval be ? GetValue(lref).
  3. Let rref be the result of evaluating ShiftExpression.
  4. Let rval be ? GetValue(rref).
  5. Let r be the result of performing Abstract Relational Comparison rval < lval with LeftFirst equal to false.
  6. ReturnIfAbrupt(r).
  7. If r is true or undefined, return false. Otherwise, return true.

The Abstract Relational Comparison seems to have special cases for strings and BigInts but I think everything else should be converting to numbers. So I would expect the second set of comparisons to be equivalent to the first and yet it's not. What am I missing?

Upvotes: 4

Views: 69

Answers (1)

Paul
Paul

Reputation: 141829

The Abstract Relational Comparison calls ToPrimitive on the arguments with a hint of 'number'. That then calls OrdinaryToPrimitive again with a hint of 'number'.

The hint here is really just a hint; it is not required that the routine returns that type. The hint of 'number' just causes OrdinaryToPrimitive to try calling valueOf before toString, but it will still end up calling toString if valueOf does not return a primitive.

If you provide a valueOf implementation that returns a primitive it will be used for the comparison:

let a=function(){}
let b=function(){}

a.valueOf = () => 1;
b.valueOf = () => 0;

console.log( a <= b );
console.log( b <= a );

By default valueOf for a function returns the function itself, which is not a primitive, so toString is called instead and the functions are compared as strings.

Upvotes: 6

Related Questions