【问题标题】:How does Javascript implement date comparison operator?Javascript如何实现日期比较运算符?
【发布时间】:2017-10-06 07:22:10
【问题描述】:
This answer 说,
Date object 会做你想做的事 - 为每个日期构造一个,然后使用 >、<、<= 或 >= 比较它们。
我没有在引用的页面上看到此功能。
- 这是特定于浏览器的行为,还是该语言的标准特性?
- 如果 Date 是 javascript“对象”,并且 javascript 不支持运算符重载,那么
>、<、<= 或 >= 运算符在 Date 对象上的这种行为是如何实现的?我的意思是,它是否在Date.prototype 上定义,是否因为定义了一些隐式转换(例如从对象到数字或字符串)而发生,它是添加到 Javascript 语言解释器/运行的 Date 对象的特例吗? -时间,还是什么?
【问题讨论】:
标签:
javascript
datetime
operator-overloading
operators
【解决方案1】:
每当您使用通常期望数字(加法、减法、较大/较小比较)的对象时,都会调用 valueOf 方法将对象转换为基元:
{ valueOf: () => 2 }.valueOf() // 2
//or not explicitly called:
+ { valueOf: () => 2 } // 2
//using the compare operator:
{ valueOf: () => 2 } < { valueOf: ()=>3 } //true as 2 < 3
因此,当您比较日期时,也会发生这种情况。由于 Date.prototype.valueOf 方法返回自 1970 年以来的毫秒数,您可以使用它来比较两个日期...
new Date().valueOf();
//or
+new Date();
//or
new Date() < new Date();
【解决方案2】:
这是特定于浏览器的行为,还是语言的标准特性?
这是一个标准功能。
如果Date 是一个javascript“对象”,并且javascript 不支持运算符重载,那么Date 上的>、<、<= 或>= 运算符的这种行为如何?对象实现了吗?
因为Date 实现valueOf 和Symbol.toPrimitive 的方式(toString 是相关的,但与您提到的特定运算符无关)。这三种方法用于通过规范中的OrdinaryToPrimitive 抽象操作将对象隐式转换为原始值的过程中,当操作员需要原始值(如字符串或数字)但接收到对象。
特别是,Date 通过返回其基础时间值(自 1970 年 1 月 1 日格林威治标准时间午夜以来的毫秒数)来实现 valueOf。所以date1 >= date2 在每个日期上调用valueOf,暗示一个数字是首选,它从日期中获取时间值(一个数字),然后比较它们。更多内容如下。
问了这个问题的副本:
我们可以为任何班级做吗?
是的,从 ES2015 开始,这都是标准的,Date 不再像以前那样特别(特别的方式相对较小,更多内容如下)。例如:
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
(上面省略了Date 的一些内容,稍后会更多。)
当操作符或类似操作需要将对象转换为基元时,它会使用对象的“到基元”操作,可选择提供有关其偏好的“提示”(字符串、数字或无偏好)。关系运算符更喜欢数字,就像- 等所有纯数学运算符一样。 + 运算符既是加法又是字符串连接,不提供提示,因此使用对象的默认值。如果没有提示,几乎内置对象默认为数字; Date 和 Symbol 默认为字符串。这过去只是内置到规范操作的逻辑中(Date;Symbol 当时不存在),但现在它通过 Symbol.toPrimitive 方法处理,对象可以覆盖该方法以提供自己的处理被转换为原语的处理。
如果我们希望 MyThing 默认为字符串而不是像 Date 那样的数字,我们将添加 Symbol.toPrimitive 方法(我还添加了 console.logs 到 valueOf 和 toString 到显示哪个用于操作):
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"
注意a + b 现在是如何进行字符串连接的,在第一个 sn-p 中它是在进行加法。那是因为我们将默认行为更改为更喜欢字符串而不是数字。