视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
JavaScript高阶教程之“==”隐藏下的类型转换
2020-11-27 21:58:58 责编:小采
文档

看完ECMAScript5.1中文版的介绍之后,相信很多小伙伴的心情应该是这样的:


别看上面说的有点花里胡哨的,其实我们可以用很简单的话来总结出来。由于本篇文章核心是“==”是如何进行类型转换,我就总结一下类型不同的情况下“==”是如何比较的。

  • 当数据类型为Boolean类型或者String类型时,比较时需要转换成Number类型。
  • 当数据类型为引用类型时,需要转换成原始数据类型。当转换后的原始数据类型为Boolean类型或者String类型,则继续转换成Number类型。
  • undefined和null跟任何值在“==”下都返回false,但二者在“==”下返回true。
  • 具体流程图如下:

    备注:

    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会抛出一个异常,提示无法将引用类型转换原始数据类型。

    具体流程图如下:

    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会抛出一个异常,提示无法将引用类型转换原始数据类型。

    具体流程图如下:


    “==”下的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

    “==”之外的类型转换

    “==”号只涉及到了ToPrimitive和ToNumber这两种转换,ToBoolean和ToString这两个没有涉及到的我们也介绍一下。

    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的类型转换,在文章的后面部分我也加上去了,希望可以给小伙伴们一个更加全面的类型转换的认识。最后附上完整的“==”类型转换的流程图:

    总结

    下载本文
    显示全文
    专题