JavaScript中的隐式类型转换

将值从一种类型转换为另外一种类型一般称为类型转换,这是显示的状况。隐式的状况称为强制类型转换,在JavaScript中存在不少隐式类型转换的状况javascript

ECMAScript数据类型

在ECMAScript中一共有7种数据类型 :

6种原始类型(基本数据类型):前端

  • Boolean
  • Null
  • Undefined
  • Number
  • String
  • Symbol (ECMAScript 6)

Object (引用类型):vue

  • Object 类型
  • Array 类型
  • Date 类型
  • RegExp 类型
  • Function 类型

基本数据类型是如何操做属性和方法的?

基本类型(基本数值、基本数据类型)是一种既非对象也无方法的数据。在 JavaScript中,共有6种基本类型:string,number,boolean,null,undefined,symbol (ECMAScript 6)。java

那为何基本数据类型没有属性和方法,咱们仍然能够对其进行一系列的操做呢?

这里就要提到原生类型内建的包装对象,包括:node

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol()

在操做原始数据类型的属性和方法时,JS会自动将原始类型转换成一个对应的包装对象。此时该对象就拥有了属于它自己的一系列属性和方法。因此当咱们定义了一些基本数据类型,想访问其对应的属性或方法时,就须要将其转换为这个值的包装对象,然而现实中并不须要这么麻烦,这是由于JS运行的宿主对象(浏览器等)将会自动地包装基本类型值来知足这样的访问。因此我认为所谓JS万物皆对象,或许只是自动的实现了一些隐式的转换。react

binlive前端开发,web开发,node,vue,react,webpack

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

关于 === 和 == 比较操做符的不一样,在比较浅显的认知中,全等操做符(===)会比较类型,而比较运算符(==)则不会比较类型。 然而咱们看一下其在MDN中的定义:webpack

全等操做符(===)比较两个值是否相等,两个被比较的值在比较前都不进行隐式类型转换。 相等操做符(==)比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。web

由此得出这两个操做符最大的不一样,就是在比较前是否会进行隐式类型转换算法

咱们也常常会见过不少隐式转换比较的题目,如:数组

undefined == null //true
[] == [] //false
[] == ![] //true
{} == !{} //false
![] == {} //false
[] == !{} //true
[1,2] == ![1] //false
复制代码

因此上述的是依据什么规则如何进行转换?在ECMA文档中的抽象相等比较算法具体描述了以下规则。

抽象相等比较算法

在比较x == y,其中 x 和 y 是值,结果返回 true 或 false 。比较规则以下:

1.当 x 的类型与 y 的类型相同时:

a. 若是x的类型为Undefined,返回true。
b. 若是x的类型为Null,返回true。
c. 若是x的类型为Number,则以下:
    i.   若是 x 为 NaN, 返回 false.(NaN == NaN 为false)
    ii.  若是 y 为 NaN, 返回 false.	
    iii. 若是 x 和 y 的值相同, 返回 true.
    iv.  若是x为+0,y为−0,返回 true.
    v.   若是x为−0,y为+0,返回 true.
          其余返回 false.
d. 若是x是string类型,那么假如x和y是彻底相同的字符序列(相同的长度和相应位置的相同字符),则返回true。不然,返回false。
e. 若是x是布尔类型,假如x和y都为true或都为false,则返回true。不然,返回false。
f. 若是x和y引用同一对象,则返回true。不然,返回false。
复制代码

2. x 和 y 类型不相同的状况:

2. 若是x为null,y为undefined,则返回true。
  3. 若是x为undefined,y为null,则返回true。(其他的任何类型与undefined和null相比都为false)
  4. 若是x是数字类型,y是字符串类型,则返回x == ToNumber(y)的比较结果.
  5. 若是x是字符串类型,y是数字类型,则返回ToNumber(x) == y的比较结果.
  6. 若是x是布尔类型,则返回ToNumber(x)==y的比较结果.
  7. 若是y是布尔类型,则返回x == ToNumber(y)的比较结果.
  8. 若是x是字符串或数字类型,y 是对象类型,返回比较结果x== ToPrimitive(y)。
  9. 若是x是对象类型,y是字符串或数字类型,返回比较结果ToPrimitive(x)==y。
  10. 其余则返回false
复制代码
上述规则看起来很复杂,但总结起来其实只有如下几点:
  1. 两个引用类型比较,只需判断它们是否是引用了同一个对象,是返回true,不然为false。
  2. undefined 和 null 二者互相比较或者与自身比较,结果是true。它俩与其余任何值比较的都为false。
  3. NaN与任何值比较包括它自身结果都是false。
  4. 引用类型和基本数据类型(String、Number)进行比较,引用类型会转换成与之所比较的基本数据的类型,再进行比较。
  5. 引用类型和基本数据类型(Boolean)进行比较,Boolean类型先转换为数字类型,引用类型再转换成与之所比较的基本数据的类型进行比较。
  6. String,Boolean,Number中的任意两个进行比较,最后都会转为Number类型再进行比较。

如今咱们再来看一下具体的类型转换过程

1. ToNumber

抽象操做ToNumber将非数字值转换为数字类型。

  • 其中布尔类型 true 转换为1,false 转换为0。
  • 字符串类型,""(空字符串)转为0,'123' 转为 123,'123px' 则被转为NaN(按照上述总结,NaN与任何类型比较都为false,此时就不须要再考虑其余状况),
  • 特殊的,undefined 会转换为 NaN,null 会转换为 0,(按上述总结第3条,null 和 undefined 跟其余任何非该两个类型中其一的比较都为false。结果容易得出,就不须要再考虑其余类型转换了)。
  • 至于引用类型,它们都须要先进行ToPrimitive转换为基本数据类型后再转换为数字类型。

2. ToPrimitive

抽象操做ToPrimitive将引用类型转为基本数据类型。

JS引擎内部转换为原始值ToPrimitive(obj,preferredType)函数接受两个参数,第一个obj为被转换的对象,第二个preferredType为但愿转换成的类型(默认为空,接受的值为Number或String)
在执行ToPrimitive(obj,preferredType)时若是第二个参数为空而且obj为Date的实例时,此时preferredType会被设置为String,其余状况下preferredType都会被设置为Number若是preferredType为Number。
转换为Number数字类型的过程为首先调用obj.valueOf(),若是执行结果是基本数值类型就返回该值,不然就调用obj.toString(),返回该字符串类型值。
转换为String数字类型的过程为首先调用obj.toString(),若是执行结果是基本数值类型就返回该值,不然就调用obj.valueOf(),返回该字符串类型值。

上述规则总结起来基本就是:

  1. ToPrimitive转换为原始数据类型时,若是是基本数据类型就直接返回该类型值。
  2. 若是不是,则优先调用valueOf方法(若是有),看其返回结果是不是基本类型,若是是,则返回。不然,再调用toString方法,转换为字符串类型后返回。
  3. 当类型为Date日期类型时,JS但愿其优先被转换成字符串类型,则先调用toString方法,转换为字符串。
  4. 其余状况报错。由于从ES5开始,使用Object.create(null)建立的对象因为没有原型链,天然就也没有valueOf()和toString()方法,这种状况出现的不多,基本能够忽略。

binlive前端开发,web开发,node,vue,react,webpack

在上图测试结果总结以下:

  • 有所的object类型转换都会转换为"[object Object]"字符串
  • 空数组[]会被转换为空字符串'', [1, 2, 3]会被转换为字符串'1,2,3',复杂类型的数组[{a: 1, b: 2}]会被转换为"[object Object],[object Object]"
  • 日期对象会转换为字符串格式,如 new Date() 会转换为'Wed Apr 17 2019 19:52:56 GMT+0800 (中国标准时间)'

由此能够看出,对象、数组、日期类型最后基本都被转换为字符串类型。因此咱们姑且能够将ToPrimitive看作将引用类型最后都转换为字符串类型。

3.ToBoolean

抽象操做ToBoolean将转为布尔类型。
对于布尔类型转换来讲,假值(undefined、null、false、+0、-0、NaN、"")会被转换为false,除假值外的全部值都会被转换成true。

4.ToString

抽象操做ToString将转为字符串类型。
对于字符串类型也比较好理解,大部分的基本数据类型转换为字符类型时,基本就是在它基础上加了引号这么直观,比较特殊的就是+0、0、-0它们几个都会被转成相同的'0',还有那些极小和极大的数字将会转换为指数形式的字符串来表现,而引用类型转换成字符串的时候也跟它们ToPrimitive时候的规则一致(见上文ToPrimitive)。

常见的隐式强制类型转换

  1. + - * / % 这几个运算符均可以将结果转为数字类型
  2. + 运算符经常使用来进行数字类型强制转换,由于它不会像**-**运算符同样影响结果
  3. + 运算符能够将日期类型new Date()转换成数字类型的时间戳
  4. ! 逻辑运算符能够将任何类型转为布尔类型,由于!运算符转换后的结果是被反转的,咱们也可使用两个!来得出正确的结果
  5. ** if()、 while() **括号内能够对全部数据类型默认执行强制布尔类型转换
  6. 三元运算符的条件也会执行强制布尔类型转换
相关文章
相关标签/搜索