奇怪的JavaScript类型转换(下)

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

接奇怪的js类型转换(中),聊一下宽松相等和严格相等,以及抽象比较关系。

宽松相等和严格相等(== 和 ===)

有人会觉的==会比===慢,实际上虽然强制类型转换切实要多花点是阿金,但仅仅是微秒级的差别而已。

抽象相等

  • NaN不等于NaN
  • +0等于-0

字符串与数字之间的相等比较

var a = 42;
var b = '42';
a === b; //false
a == b; //true
复制代码

上面相等的原因,ES5规范是这样定义的

  1. 如果Type(x)是数字, Type(y)是字符串,则返回 x == ToNumber(y)的结果。
  2. 如果Type(x)是字符串, Type(y)是数字,则返回 ToNumber(x) == y的结果。

与布尔类型的相等比较

var a = '42';
var b = true;
a == b; //false
复制代码

规范是这样定义的

  1. 如果 Type(x)是布尔类型,则返回 ToNumber(x) == y 的结果。
  2. 如果 Type(y)是布尔类型,则返回 x == ToNumber(y) 的结果。

null和undefined的相等比较 (==)

  1. 如果x为null,y为undefined,则结果为true。
  2. 如果x为undefined, y为null,则结果为true。
var a = null;
var b;
a == b; //true
a == true; //true
b == null; //true
a == false; //false
b == false; // false
a == '' // false
b == '' //false
a == 0 //false
b == 0 //false
复制代码

null和undefined之间的强制类型转换是安全可靠的。
对象和非对象之间的想等比较
规范定义:

  1. 如果Type(x)是字符串或数字,Type(y)是对象,则返回x == ToPrimitive(y)的结果;
  2. 如果Type(x)是对象,Type(y)是字符串或数字,则返回ToPrimitive(x) == y的结果;
var a = 24;
var b = [24];
a == b; //true
复制代码

少数情况

返回其他数字

Number.prototype.valueOf = function() {return 3}
new Number(2) == 3;
//有这个就可以做这样的判断,看似不可能
var i = 2;
Number.prototype.valueOf = function() {return i++}
var a = new Number(24);
if(a == 2 && a == 3) {}
复制代码

假值的相等比较

console.log('0' == null); // false
console.log('0' == undefined); //false
console.log('0' == false); //true *
console.log('0' == NaN); // false
console.log('0' == 0); // true
console.log('0' == ''); // false
console.log(false == null); // false
console.log(false == undefined); // false
console.log(false == NaN); // false
console.log(false == 0); // true *
console.log(false == ''); // true *
console.log(false == []); // true  *
console.log(false == {}); // false
console.log('' == null); // false
console.log('' == undefined); //false
console.log('' == NaN); // false
console.log('' == 0); // true *
console.log('' == []); // true *
console.log('' == {}); // false
console.log(0 == null); //false
console.log(0 == NaN); // false
console.log(0 == []); // true *
console.log(0 == {}); // false
复制代码

这里面打星号的属于假阳情况,并不是很好理解。
极端情况

//终于看到开头说的情况
[] == ![] // true
复制代码

这里是根据toBoolean规则,他会进行布尔值显式强制类型转换,所以[]==![]变成了[] == false,根据前面讲过 false == [],最后就理解的通了。
还有其他情况

2 == [2]; // true
"" == [null]; // true
复制代码

介绍toNumber时讲过,== 右边的值[2] 和[null]会进行toPrimitive强制类型转换,以便能够和左边的基本类型(2和””)进行比较。因为数组的valueOf()返回数组本身,所以强制类型转换过程中熟组会进行字符串化。最后可以认为是2 == 2, “” == “”。
还有一个坑常常被提到:

0 == "\n"; // true, "\n",""等空字符串被toNumber强制类型转换为0.
复制代码

安全运用隐式强制类型转换

  1. 如果两边的值中有true或者false, 千万不要使用==;
  2. 如果两边的值有[]、””或0,尽量不要使用==;

这时候用===来避免不经意的强制类型转换,这两个原则可以让我们避开几乎所有强制类型转换的坑。
有一种情况下,强制类型转换是绝对安全的,那就是用typeof操作,typeof总是返回七个字符串之一,其中没有空字符串,所以在类型检查过程中不会发生隐式强制类型转换。typeof x == “function”是100%安全的,和typeof x === “function” 一样。

抽象关系比较

分为两个部分:比较双方都是字符串和其他情况。
比较双方首先调用toPrimitive,结果出现非字符串,就根据toNumber规则将双方强制类型转换为数字来比较

var a = [42];
var b = ['43'];
a < b; // true
b < a; // false
复制代码

如果比较双方都是字符串,按字母顺序进行比较;

var a = ["42"];
var b = ["043];
a < b; // false
// 这里分别以"4"和"0"开头,因为0在字母顺序上小于 "4",所以最后结果为false。
// 如果你非要他们比较数字的话可以像下面这样
Number(a) < Number(b)
// 同理
var a = [4,2];
var b = [0,4,3];
a < b; // false
//两个都转为字符串,以首字母排序比大小
var a = {b : 42};
var b = {b : 43};
a < b; //false
因为这两个都会转化为[object Object],所以按照字母顺序 a < b,并不成立
复制代码

下面的例子就有些奇怪了

var a = {b:42};
var b = {b:43};
a < b; // false
a == b; // false
a > b; // false
a <= b; // true
a >= b; // true
复制代码

因为根据规范 a <= b 被处理为 b < a,然后将结果反转,因为 b < a 的结果是false,所以a <= b 的结果是true。
这可能跟我们想的大相庭径,即 <= 应该是”小于或者等于”,实际上js中的<=是不大于的意思,即!(a > b), 处理为!(b < a),同理, a >= b 处理为 !(b <= a)。

最后

总结一下,以上讲了强制类型转换,分两种显式和隐式。我们写代码的时候有时候为了避免一些不知道的判断错误,可通过强制类型转换去判断。大家如果还有什么问题,可以评论区探讨,今天到此为止啦!!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享