JavaScript类型与类型转换
引子
ECMAScript的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个用于保存值的占位符而已。
由于JavaScript是一种松散类型的语言,即变量在使用时,并不需要事先知道它的类型。因此不同变量间的比较往往要作类型转换,这也是一些常见quiz的由来。
比如下面的一道面试题:
1 | //请写出下面语句的输出结果 |
怎么样?是不是有点晕,下面我们一部分一部分地来解释JavaScript中一些类型和相等相关的“潜规则”。
数据类型
让我们先从JavaScript的数据类型开始。JavaScript中只有5种基本类型和引用类型。其中5种基本类型分别是:
- Undefined
- Null
- Number
- Boolean
- String
除此之外只有1种引用类型——Object,Object本质上是由一组无序的键值对组成。5种基本类型是按值访问的,引用类型Object是按引用访问的。
可以使用typeof操作符监测变量的基本类型。*它可以判断变量是否为除null的其他5种基本类型以及function类型。除此之外都会返回”object”*。之所以null的typeof结果也为”object”,是因为null实际上表示引用指向空对象。
使用instanceof可以判断引用类型的具体值。使用方法类似于A instanceof B的形式。当B为“Object”时,表达式永远返回true。因为根据规定,所有引用类型的值都是Object的实例。
下面是几个例子。通过instanceof操作符可以很方便地区分空数组和空对象(当然还有Object.prototype.toString.call()和[].concat()两种方法。)
1 | console.log([] instanceof Array); // true |
类型转换
to Boolean类型
Boolean类型是ECMAScript中使用最多的类型之一。类型只有true和false两个字面量。true不一定等于1,false也不一定等于0.可以通过调用Boolean()函数将其他类型转型为Boolean类型。规则如下:
- String类型:非空字符串=>true,空字符串=>false
- Number类型:非零数字(包括Inifity)=>true, 0和NaN=>false
- Object类型:任何对象=>true, null=>false
- Undefined:false
在使用if()语句或三元操作符等情况要求Boolean类型时,括号内的表达式将会自动使用Boolean()函数转换为布尔类型。
to String类型
有两种方法可以将值转为字符串,一种是使用几乎所有值都有的toString方法,对于null和undefined使用另一种——String()函数。
前者适用于除null和undefined外的所有值,甚至String本身(返回一个自身的副本)。有些toString()方法接收一个基数作为参数(如Number)对Object使用toString方法时,会根据对象内toString的定义决定。
- Array返回逗号隔开的不包括外侧中括号的字符串
- Function返回Function定义的字符串
- 普通Object返回”[object Object]”
- null和undefined分别返回”null”和”undefined”
to Number类型
可以使用Number(), parseInt()和parseFloat()三个函数做强制转换。转换到Number类型的规则要更好理解些。
- 是Boolean类型时,true和false分别转换到1和0
- 数字类型时,返回本身
- null时返回0
- undefined时返回NaN
- 对字符串使用类似于parseInt和parseFloat类似的方法(可以识别0x这样的进制前缀甚至Infinity这样的字符串)
- 对象使用valueOf()方法,再使用之前的规则;如果结果是NaN,再使用toString()方法作转换
类型转换场景
一元加减
一元加减只需对操作数强制转换到Number类型。向下面这样的例子:
1 | var s1 = "01"; |
加性操作符
ECMAScript中规定的加减法这两个操作符有一些特殊行为,不仅处理数值的加减,还处理字符串的加减。因此转换规则还有些复杂。
加法
优先做数值加减,无法完成时做字符串拼接。两个操作数都是数值时,执行常规的加法计算。
- 一个操作数为NaN时,返回NaN
- Inifity + -Inifity,返回NaN
- +0 加 -0,返回+0
只要有一个操作数为字符串类型,应用下面规则:
- 两个都是字符串时,则将它们拼接起来。
- 一个是字符串时,先将另一个转换为字符串
布尔值和null以及undefined在另一个操作数是数值类型时转换为数值类型,反之转换为字符串类型。
一个操作数为对象时,转换为字符串类型。
减法
与加法类似,除了数值相减减法也需要做一些类型转换。但是和加法不一样的是,减法返回的一定是Number类型。
- 一个数值为NaN时,结果为NaN
- 同号的Infinity相减返回NaN(如Infinity - Infinity),异号的Infinity相减等于第一个操作数
- 除了-0减+0返回-0,其余0间相减均返回+0
- 操作数出现字符串、布尔值、null、undefined时,做Number转换再进行数值减法
- 对象先尝试用valueOf方法获得对象数值,若无此方法则调用toString方法,并转换得到的字符串。
关系操作符
关系操作符即大于(>)、小于(<)、大于等于(>=)和小于等于(<=)。在操作数并非纯数值时,ECMAScript也会进行数据转换或一些奇怪的操作。
- 两个操作数都是数值时,进行数值比较
- 两个操作数都是字符串时,按照对应字符编码顺序比较
- 一个操作数是数值时,转换另一个为数值再比较
- 一个操作数是对象时,优先使用valueOf方法比较数值,没有该方法时再使用toString方法
- 任何数和NaN比较都会返回false
相等和全等
相等和全等用于确认两个变量是否相等。对此ECMAScript提供两组操作符:-相等-和-全等-。相等先转换类型后比较,全等仅比较不转换类型。由于情况较多较复杂,这里单独列一节。
ECMAScript中相等操作符为==。不相等操作符为!=。它们都会先强制转型变量再相互比较。转换规则如下:
- 先将布尔值转换为数值,false转换为0,true转换为1
- 字符串和数值比较时,将字符串转换为数值
- 两个操作数都是对象时,判断它们是否指向同一个对象(只比较引用)
- 只有一个操作数是对象时,调用valueOf()或toString()方法获得基本类型值
- null和undefined是相等的
- null和undefined在比较时不会被转换
- NaN出现时,相等操作符返回false
全等操作符为===,对象的不全等操作符为!==。它们不会转换变量类型,相比较类型后比较值。因此行为更容易预测。