欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否

程序员文章站 2022-04-09 09:21:40
抛砖引玉 按照正常的逻辑来说,我们判断两个值是否相等会遵循以下规则:但是我看下面一组值: []==0 //true []==false //true []==!{} //true...

抛砖引玉

按照正常的逻辑来说,我们判断两个值是否相等会遵循以下规则:
JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否
但是我看下面一组值:

[]==0 //true
[]==false //true
[]==!{} //true
[10]==10 //true
'0'==false //true
''==0 //true
undefined==null //true 
!null==true //true 

JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否
居然没有按照我们的剧本走,那它比较规则又是什么?下面我就来分析一波。

“==”的比较规则

首先我们先去ecmascript5.1中文版( https://lzw.me/pages/ecmascrip... )找一下“==”的比较规则,如下:

1.若type(x)与type(y)相同, 则
    a.若type(x)为undefined, 返回true。
    b.若type(x)为null, 返回true。
    c.若type(x)为number, 则
        i.若x为nan, 返回false。
        ii.若y为nan, 返回false。
        iii.若x与y为相等数值, 返回true。
        iv.若x 为 +0 且 y为?0, 返回true。
        v.若x 为 ?0 且 y为+0, 返回true。
        vi返回false。
    d.若type(x)为string, 则当x和y为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true。 否则, 返回false。
    e.若type(x)为boolean, 当x和y为同为true或者同为false时返回true。 否则, 返回false。
    f.当x和y为引用同一对象时返回true。否则,返回false。
2.若x为null且y为undefined, 返回true。
3.若x为undefined且y为null, 返回true。
4.若type(x) 为 number 且 type(y)为string, 返回comparison x == tonumber(y)的结果。
5.若type(x) 为 string 且 type(y)为number,返回比较tonumber(x) == y的结果。
6.若type(x)为boolean, 返回比较tonumber(x) == y的结果。
7.若type(y)为boolean, 返回比较x == tonumber(y)的结果。
8.若type(x)为string或number,且type(y)为object,返回比较x == toprimitive(y)的结果。
9.若type(x)为object且type(y)为string或number, 返回比较toprimitive(x) == y的结果。
10.返回 false

看完ecmascript5.1中文版的介绍之后,相信很多小伙伴的心情应该是这样的:
JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否
别看上面说的有点花里胡哨的,其实我们可以用很简单的话来总结出来。由于本篇文章核心是“==”是如何进行类型转换,我就总结一下类型不同的情况下“==”是如何比较的。

当数据类型为boolean类型或者string类型时,比较时需要转换成number类型。 当数据类型为引用类型时,需要转换成原始数据类型。当转换后的原始数据类型为boolean类型或者string类型,则继续转换成number类型。 undefined和null跟任何值在“==”下都返回false,但二者在“==”下返回true。

具体流程图如下:

JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否

备注:

javascript的数据类型可以分为以下两种:

原始数据类型(null、undefined、number、string、boolean、symbol(es6才引入的)) 引用类型 (object)

boolean类型、string类型转换成number类型的规则(tonumber)

boolean类型

boolean number
true 1
false 0

string类型

标准的数字格式

如果是标准的数字格式,转换成number类型相比不用多说,比如下面这几个栗子??:

"123" => 123
"12.34" => 12.34
"0.12" => 0.12
"-12.34" => -12.34

非标准的数字格式

但是如果是非标准的数据格式,要分两种情况来考虑:

第一种:只包含数字并且最多只有1个点。 第二种:包含非数字以及含有多个1个点。

只包含数字并且最多只有1个点

这种情况下会首先进行补0和去0的操作,下面看几个栗子??:

"01234" => 1234
".1" => 0.1
"12." => 12
"1.000" => 1
"-02.30" => -2.3

包含非数字以及含有多个1个点

这种情况下统统返回nan,意为“not a number”,这里我们要注意一下,nan还是number类型,下面看几个栗子??:

"123aa4" => nan
"哈喽,dd" => nan
typeof nan => "numer"

引用类型转换成原始数据类型的规则(toprimitive)

在介绍转换规则之前,首先我们我们介绍一下引用类型都带有的两个方法:

object.prototype.tostring object.prototype.valueof

这二者都可以将引用类型转换成原始数据类型,接下来我们对二者做一个详细的介绍:

object.prototype.tostring

mdn是这样解释的:

the tostring() method returns a string representing the object.(tostring()这个方法返回一个代表这个对象的字符串)

举个栗子??:

const obj = {}
console.log(string(obj))  //"[object object]"
obj.tostring = function(){
    return 'hello,teacher cang!'
}
console.log(string(obj)) //"hello,teacher cang!"

object.prototype.valueof

mdn是这样解释的:

the valueof() method returns the primitive value of the specified object.( valueof()这个方法返回这个对象的原始数据值)

举个栗子??:

const obj = {}
console.log(number(obj)) //nan
obj.valueof = function(){
    return 12345;
}
console.log(number(obj)) //12345

valueof和tostring的优先级

关于这二者的优先级,在不同的情况下有着不同的优先级,下面我们根据不同情况介绍一下。

tonumber

看个栗子??:

const obj = {
    tostring(){
        console.log('调用了tostring');
        return 'hello,teacher cang!';
    },
    valueof(){
        console.log('调用了valueof');
        return 12345;
    }
}
console.log(number(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueof
12345

通过上面的代码的运行结果,我们得出这么一个结论:

在tonumber情况下,valueof的优先级大于tostring。

接下里我们看这么一种情况,如果valueof返回的并不是原始数据类型会怎么样。

const obj = {
    tostring(){
        console.log('调用了tostring');
        return 12345;
    },
    valueof(){
        console.log('调用了valueof');
        return {};
    }
}
console.log(number(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueof
调用了tostring
12345

从上面的运行结果中,我们可以得出这么一个结论:

在tonumber情况下,如果valueof返回的不是原始数据类型,则会自动调用tostring。

打破砂锅问到底,再来一个非常极端的情况,如果说valueof和tostring返回的都不是原始数据类型,这时又该怎么样呢?同样,我们继续看一下运行结果:

const obj = {
    tostring(){
        console.log('调用了tostring');
        return {};
    },
    valueof(){
        console.log('调用了valueof');
        return {};
    }
}
console.log(number(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueof
调用了tostring
uncaught typeerror: cannot convert object to primitive value

从上面的运行结果中,我们再次可以得出这么一个结论:

在tonumber情况下,如果valueof和tostring返回的都不是原始数据类型,那么js会抛出异常,提示无法将引用类型转换原始数据类型。

根据三组代码的运行的结果,我们最后总结一下:

在tonumber情况下,js会优先调用valueof,如果valueof返回的不是原始数据类型,则会接着调用tostring,如果tostring返回的也不是原始数据类型,js会抛出一个异常,提示无法将引用类型转换原始数据类型。

具体流程图如下:

JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否

tostring

看个栗子??:

const obj = {
    tostring(){
        console.log('调用了tostring');
        return 'hello,teacher cang!';
    },
    valueof(){
        console.log('调用了valueof');
        return 12345;
    }
}
console.log(string(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了tostring
hello,teacher cang!

通过上面的代码的运行结果,我们得出这么一个结论:

在tostring情况下,tostring的优先级大于valueof。

同样我们看一下,tostring返回的值不是原始数据类型时会怎样:

const obj = {
    tostring(){
        console.log('调用了tostring');
        return {};
    },
    valueof(){
        console.log('调用了valueof');
        return 'hello,teacher cang!';
    }
}
console.log(string(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了tostring
调用了valueof
hello,teacher cang!

根据运行结果我们可以得出:

在tostring情况下,如果tostring返回的不是原始数据类型,则会自动调用valueof。

最后我们看一下极端情况,二者返回的都不是原始数据类型:

const obj = {
    tostring(){
        console.log('调用了tostring');
        return {};
    },
    valueof(){
        console.log('调用了valueof');
        return {};
    }
}
console.log(string(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了tostring
调用了valueof
uncaught typeerror: cannot convert object to primitive value

我们又发现:

在tostring情况下,如果tostring和valueof返回的都不是原始数据类型,那么js会抛出异常,提示无法将引用类型转换原始数据类型。

我们将三个结论综合一下:

在tostring情况下,js会优先调用tostring,如果tostring返回的不是原始数据类型,则会接着调用valueof,如果valueof返回的也不是原始数据类型,js会抛出一个异常,提示无法将引用类型转换原始数据类型。

具体流程图如下:
JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否

“==”下的valueof和tostring的优先级

从文章前面我们总结出来“==”的比较流程来看,引用类型转换成原始数据类型之后,如果是sting类型的话,还要再次转成number类型,因此“==”下的引用类型转换原始数据类型过程中,遵循tonumber的优先级规则。

const obj = {
    tostring(){
        console.log('调用了tostring');
        return 'hello,teacher cang!';
    },
    valueof(){
        console.log('调用了valueof');
        return 12345;
    }
}
console.log(obj==12345); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueof
true
const obj = {
    tostring(){
        console.log('调用了tostring');
        return 12345;
    },
    valueof(){
        console.log('调用了valueof');
        return {};
    }
}
console.log(obj==12345); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueof
调用了tostring
true
const obj = {
    tostring(){
        console.log('调用了tostring');
        return {};
    },
    valueof(){
        console.log('调用了valueof');
        return {};
    }
}
console.log(obj==12345); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了tostring
调用了valueof
uncaught typeerror: cannot convert object to primitive value

“==”之外的类型转换

“==”号只涉及到了toprimitivetonumber这两种转换,tobooleantostring这两个没有涉及到的我们也介绍一下。

toboolean

对于toboolean,我们只需要记住几个特例是转成false的,其余的皆为true。

boolean('') => false
boolean(undefined) => false
boolean(null) => false
boolean(0) => false 

tostring

number to string

number转string之前,首先会做一个去0和补0的操作,然后再去转成string类型。

string(1.234) => "1.234"
string(nan) => "nan"
string(.1234) => "0.1234"
string(1.23400) => "1.234"

boolean to string

string(true) => "true"
string(false) => "false"

null和undefined to string

string(null) => "null"
string(undefined) => "undefined"

引用类型 to string

引用类型先toprimitive转换成原始数据类型,若转换后的原始数据类型不是string类型,再做string类型的转换。

const obj = {
    tostring(){
        console.log('调用了tostring');
        return true;
    }
}
console.log(string(obj))

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了tostring
"true"

总结

“==”下的类型转换,要分为两种情况来考虑。第一种,原始数据类型;第二种,引用类型。原始数据类型中string类型和boolean类型是需要转换成number类型再去比较的,而引用类型则需要先转换成原始数据类型再进行后续的转换。搞清整个流程之后,我们再去理解“==”涉及到的tonumber和toprimitive是如何进行转换的。按照这样子的理解步骤走,我们对“==”隐藏下的类型转换会有比较清晰的认识。另外,“==”不涉及到tostring和toboolean的类型转换,在文章的后面部分我也加上去了,希望可以给小伙伴们一个更加全面的类型转换的认识。最后附上完整的“==”类型转换的流程图:

JavaScript高阶副本:“==”隐藏下的类型转换-个人文章-SegmentFault思否