Is this browser-specific behaviour, or a standard feature of the language?
It's a standard feature.
If Date
is a javascript "object", and javascript doesn't support operator overloading, then how is this behaviour of the >
, <
, <=
or >=
operator on Date
objects implemented?
Because of the way Date
implements valueOf
and Symbol.toPrimitive
(toString
is related, but not for the specific operators you mentioned). Those three methods are used in the process of implicitly converting an object to a primitive value by the OrdinaryToPrimitive abstract operation in the specification, which is used by operators when they need a primitive value (like a string or number) but they receive an object.
In particular, Date
implements valueOf
by returning its underlying time value (milliseconds since Jan 1st 1970 at midnight GMT). So date1 >= date2
invokes valueOf
on each of the dates, hinting that a number is preferred, which gets the time value (a number) from the dates, and then compares them. More below.
A duplicate of this question asked:
Can we do it for any class?
Yes, as of ES2015 this is all standard and Date
is no longer special like it used to be (the way it was special was relatively minor, more below). For example:
class MyThing {
constructor(value) {
this.value = value;
}
valueOf() {
return this.value;
}
toString() {
return String(this.value);
}
}
const a = new MyThing(27);
const b = new MyThing(42);
console.log(`a < b? ${a < b}`); // a < b? true
console.log(`b < a? ${b < a}`); // b < a? false
console.log(`b - a = ${b - a}`); // b - a = 15
console.log(`a + b = ${a + b}`); // a + b = 69
(The above leaves out something Date
has, more in a moment.)
When an operator or similar needs to convert an object to a primitive, it uses the object's "to primitive" operation, optionally providing a "hint" about what it would prefer (a string, a number, or no preference). The relational operators prefer numbers, as do all the pure math operators like -
. The +
operator, being both addition and string concatenation, doesn't provide a hint so the object's default is used. Almost built-in objects default to number if there's no hint; Date
and Symbol
default to string instead. That used to just be built into the logic of the specification's operation (for Date
; Symbol
didn't exist then), but it's now handled via the Symbol.toPrimitive
method instead, which an object can override to provide its own handling of being converted to a primitive.
If we wanted MyThing
to default to string instead of number like Date
does, we'd add the Symbol.toPrimitive
method (I've also added console.log
s to valueOf
and toString
to show which is used for the operations):
class MyThing {
constructor(value) {
this.value = value;
}
valueOf() {
console.log("valueOf");
return this.value;
}
toString() {
console.log("toString");
return String(this.value);
}
[Symbol.toPrimitive](hint) {
if (hint === "number") {
return this.valueOf();
}
// "default" or "number"
return this.toString();
}
}
const a = new MyThing(27);
const b = new MyThing(42);
console.log(`a < b? ${a < b}`); // a < b? true
console.log(`b < a? ${b < a}`); // b < a? false
console.log(`b - a = ${b - a}`); // b - a = 15
console.log(`a + b = ${a + b}`); // a + a = "2742"
Notice how a + b
is doing string concatenation now, where in the first snippet it was doing addition. That's because we changed the default behavior to prefer strings over numbers.