我们看看下面的表达式是什么?
"1.0e0" == { valueOf: function () { return true;}}
这两个看似无关的值使用==运算符实际上是相等的。就像1.3描述的隐式强制转换一样,在比较之前,他们都被解析为数字1,而匿名对象也通过valueOf方法得到的结果为true,然后再转换为数字,得到1。
很容易使用这些强制转换完成一些工作。例如,从一个Web表单读取一个字段并与一个数字进行比较。
var today = new Date();
if (form.month.value == (today.getMonth() + 1) && form.day.value == today.getDate()) {
//happy birthday!
//...
}
但实际上,它只是显式地使用Number函数或者一元运算符 + 将值转换为数字。
var today = new Date();
if (+form.month.value == (today.getMonth() + 1) && +form.day.value == today.getDate()) {
//happy birthday!
//...
}
转换测试:
var num = '1';
console.log (typeof num) // string
console.log (typeof +num) // number
一个更好的替代方式是使用严格的相等运算符 ===。
var today = new Date();
if (+form.month.value === (today.getMonth() + 1) && +form.day.value === today.getDate()) {
//happy birthday!
//...
}
当两个参数属于同一类型时, == 和 === 运算符的行为是没有区别的。但最好还是使用严格的相等运算符, 因为读者会非常清晰地知道: 在比较操作中并没有涉及任何转换。否则,我们需要读者准确地记住这些强制转换规则以解读代码的行为。
事实上,这些强制转换规则一点也不明显。表1.1包含了 == 运算符针对不同类型参数的强制转换规则。这些规则具有对称性。例如,第一条规则既适用于null == undefined,也适用于undefined == null。在很多时候, 这些转换都试图产生数字。 但当他们处理对象时会变得难以捉摸。 操作符试图将对象转换为原始值, 可通过调用对象的valueOf 和 toString 方法而实现。 更令人难以捉摸的是, Date对象以相反的顺序尝试调用这两个方法。
==运算符将数据以不同的表现呈现出来, 这种纠错方式有时称为“照我的意思去做”(do what I mean)的语义。但计算机并不能真正地了解我们的心思。世界上有太多的数据表现形式,JavaScript需要知道我们使用的是哪种。例如,我们可能希望能将一个包含日期的字符串和一个Date对象进行比较。
var date = new Date('1999/12/31');
date == '1999/12/31' // false
这个例子失败是因为Date对象被转换成一种不同格式的字符串, 而不是本例所采用的格式。
date.toString(); // Fri Dec 31 1999 00:00:00 GMT+0800
但是,这种错误是一个更普遍的强制转换误解的“症状”。==运算符并不能推断和统一所有的数据格式。它需要我们和读者都能理解其微妙的强制转换规则。更好的策略是显式自定义应用程序转换的逻辑,并使用严格相等运算符。
var date = new Date('1999/12/31');
function toYMD (date) {
var y = date.getYear() + 1900, //year is 1900-indexed
m = date.getMonth() + 1, //month is 0-indexed
d = date.getDate();
return y + '/' + (m < 10 ? '0' + m : m) + '/' + (d < 10 ? '0' + d : d);
}
toYMD(date) === '1999/12/31'; // true
显式地定义转换的逻辑能确保我们不会混淆==运算符的强制转换规则, 而且免除了读者不得不查找或记住这些规则的麻烦。
提示
- 当参数类型不同时, == 运算符应用了一套难以理解的隐式强制转换规则。
- 使用 === 运算符, 使读者不需要涉及任何的隐式强制转换就能明白我们的比较运算。
- 当比较不同类型的值时, 使用我们自己的显式强制转换使程序的行为更清晰。