ECMAScript有6种简单的数据类型(也称为原始类型):undefined、Null、Boolean、Number、String和Symbol。Symbol是ES6中新增的。还有一种复杂的数据类型叫做Object。Object是一种无序名值对的集合。因为在ECMAScript中不能定义自己的数据类型,所有值都可以使用上述7种数据类型之一来表示。只有7种数据类型似乎不足以表示全部数据。但是ECMPScript的数据类型很灵活。一种数据类型可以当做多种数据类型来使用。
typeof操作符
因为ECMAScript的类型系统是松散的,所以需要一种手段来确定任意变量的数据类型。typeof操作符就是为此而诞生的。对一个值使用typeof操作符会返回下列字符串之一:
- “undefined”:表示值未定义
- “boolean”:表示值为bool值
- “string”:表示值为字符串
- “number”:表示值为数值
- “object”:表示值为对象,或者null
- “function”:表示值为函数
- “symbol”:表示值为符号
下面是typeof的操作符的例子:
let message='some string'
console.log(typeof(message)) //"string"
console.log(typeof(message)) //"string"
console.log(typeof 95) //"number"
在这个例子中。我们把一个变量(message)和一个数值字面量传递给了typeof操作符。注意,因为typeof是一个操作符而不是函数。所以不需要参数。但是可以使用参数
注意typeof在某些返回的情况下结果可能会让人费解。但是技术上讲还是正确的。比如,调用typeof null返回的是Object。这是因为特殊的值null被认为是一个空对象的引用
注意,严格来讲,函数在ECMAScript中被认为是对象。并不代表一种数据类型。可是,函数也有自己特殊的属性。为此,就有必要通过typeof操作符来区分函数和其他对象。
Undefined类型
Undefined类型只有一个值,那就是默认值undefined。当使用var或者let声明了变量但是没有初始化时,就相当于给变量赋值了undefined值;
let message;
console.log(message==undefined); //true
在这个例子中,变量message在声明的时候并未初始化。而在比较它和undefined的字面值时。两者是相等的。这个例子等同于如下示例
let message=undefined;
console.log(message==undefined) //true
这里,变量message显示的以undefined来初始化。但是这是不必要的,因为默认情况下。任何未经初始化的变量都会得到undefined值。
注意,一般来说,永远不要用显式的给某个变量设置undefined值。字面值undefined主要用于比较。而且在ECMA-262第三版之前是不存在的。增加这个特殊值的目的就是为了明确空指针对象和未初始化变量的区别
注意,包含undefined值的变量跟未定义变量是有区别的,请看下面的例子:
let message; //这个变量被声明了,只是值为undefined
//确保没有声明过这个变量
// let age
console.log(message) //"undefined"
console.log(age) // 报错
在上面的例子中,第一个console.log会指出变量的message值,即undefined,而第二个console.log()要输出一个未声明的变量age的值,因此会导致报错。对于未声明的变量,只能执行一个有用的操作,就是对他调用typeof。(对于未声明的变量使用delete也不会报错。但是这个操作没什么用,并且在严格模式下也会报错)
在对未初始化的变量调用typeof的时候,返回的结果就是undefined,但未声明的变量调用typeof的时候,返回的结果还是“undefined”,这就让人有点看不懂了。比如下面的例子:
let message; //这个变量被声明了,只是值为undefined
// 确保没有声明过这个变量
// let age
console.log(typeof message) //undefined
console.log(typeof age) //undefined
undefined是一个假值,因此,如果需要,可以使用更加简洁的方式检测undefined。不过需要记住,也有很多其他的值可能是假值。所以一定要明确自己想监测的就是undefined这个字面值,而不仅仅是假值。
let message; //这个变量被声明了,只是值为undefined
//age没有声明
if(message){
//这个块不会被执行
}
if(!message){
//这个块会被执行
}
if(age){
//这里会报错
}
Null类型
Null类型同样只有一个值,即特殊值null。逻辑上讲,null值表示一个空对象指针,这也只是给typeof传一个null会返回object的原因:
let car=null;
console.log(typeof car);
在定义将来要保存的对象值的变量时,建议使用null来初始化。不要使用其他值。这样,只要检查这个变量的值是否是null,就可以知道这个变量是否在后来被重新赋予了一个对象的引用。比如:
if(call !=null){
//car是一个对象的引用
}
undefined是由null值派生而来的,因此ECMA-262将他们定义为表面上相等,如下面的例子所示:
console.log(null==undefined) //true
用等于操作符(==)比较null和undefined始终返回true。但是要注意,这个操作符会为了比较而转换他的操作数。
即使null和undefined有关系,他们的用途也是完全不一样的。如前面所述,永远不必显示的将变量值设置为undefined。但是null不是这样的。任何时候,只要变量保存对象,而当时又没有那个对象可保存。就要用null来填充该变量。这样就可以保持null是空对象指针的语义,并进一步将其与undefined区分开来
null是一个假值,因此,如果需要,可以用更加简洁的方法去监测他,不过要记住,也有很多其他可能的值同样是假值。所以一定要明确自己想监测的就是null这个字面值。而不仅仅是假值。
let message=null;
let age;
if(message){
//这个块不会执行
}
if(!message){
//这个块会执行
}
if(age){
//这个块不会执行
}
if(!age){
//这个块会执行
}
Boolean类型
Boolean类型是ECMAScript中使用最频繁的类型之一,有两个字面值:true和false。这两个bool值不同于数值,因此true不等于1,false不等于0。下面是给变量赋值bool的例子:
let found=false;
let lost=false;
注意,bool值字面量true和false是区分大小写的,因此True和False是有效的标识符,但不是bool值。可以调用特定的Boolean转型函数;
let message="Hello world"
let messageAsBoolean=Boolean(message);
在这个例子中,字符串message会被转换为bool值并保持在变量messageAsBoolean中,Boolean()转型函数可以在任意数据类型的数据上调用,而且始终返回一个布尔值。什么值能够转换为true和false的规则取决于数据类型和实际的值。下表总结了不同类型和布尔值之间的转换规则。
- Boolean转换为true的值为true,转换为false的值为false
- String转换为true的值为非空字符串,转换为false的值为空字符串
- Number转换为true的值包括无穷值,转为为false的值为NaN
- Object转换为true的值为任意对象,转换为false的值为null
- Undefined转为true的值为N/A,转换为false的值为undefined
理解以上转换非常重要,因为像if等流控制语句会自动执行其他类型值到bool值的转换。例如:
let message= "Hello world"
if(message){
console.log("Value is true");
}
在这个例子中,console.log()会输出字符串“Value is true”,因为字符串message会被自动转换为等价的true。由于这种自动转换,理解流控制语句中使用的是什么变量就非常的重要。错误的使用对象而不是bool值会明显的改变程序的执行流。
Number类型
ECMAScript中最有意思的数据类型或许就是Number了。Number类型使用IEEE 754格式表示整数和浮点数(在某些语言中也叫做双精度数)。不同的数值类型也有不同的数值字面量格式。
最基本的数值字面量格式是十进制整数:
let intNum=55; //整数
整数也可以用8进制,或者16进制字面量表示。对于八进制字面量。第一个数字必须是0。然后是对应的8进制数字。如果字面量超出了应有的范围。就会忽略前面的0,后面的数字序列会被当成10进制数,如下所示
let octalNum1=070 //八进制56
let octalNum2=079 //无效的8进制值,当成79处理
let octalNum3=08 //无效的8进制值,当成8处理
ECMAScript2015或者ES6中的八进制通过前缀0o来表示;严格模式下,前缀0会被视为语法错误,如果要使用8进制值,应该使用前缀0o
要创建16进制字面量,必须让真正的数值前缀0x区分大小写,然后是16进制数字(0-9和A-F)。16进制中的字母大小写均可。下面是几个例子:
let hexNum=0xA //16进制10
let hexNum2=0x1f //16进制31
使用8进制和16进制格式创建的数值在所有数学计算中都被视为10进制数值。
注意:由于JavaScript保存数值的方式,实际中可能存在+0和-0。正0和负0在所有情况下都认为是同等的。这里特地说明一下。
浮点值
要定义浮点值。数值中必须包含小数点,而且小数点后面必须至少有一个数字,虽然小数点后面不是必须有整数。但是推荐加上。下面是几个例子。
let floatNum1=1.1
let floatNum2=0.1
let floatNum3=.1 //有效 但是不推荐
因为存储浮点值使用的内存空间是存储整数值的两倍,所以ECMAScript总会想方设法吧值变为整数。在小数点后面没有数字的情况下。数值就会变成整数。类似的,如果说数值本身就是整数,但是后面有个.0那么他也会变成整数。如下所示
let floatNUm1=1.;
let floatNum2=10.0;
对于非常大或者非常小的数值,浮点数可以用科学技术法来表示。科学计数法用于表示一个应该乘以10的给定次幂的数值。
ECMAScript中科学技术法的格式要求是一个数值(整数或者浮点数)后跟一个大写或小写的字母e,再加上一个要乘以10的多少次幂。比如:
let floatNum=3.1234e5 //等于31234
在这个例子中,floatNum等于3.1234,只不过科学技术法显得更加的简洁。这种表示方法实际上相当于在说:"以3.125作为系数,乘以10的7次幂"
科学技术法也可以表示非常小的数值,列如0.000 000 000 000 003。默认情况下,ECMAScript会将小数点后面至少包含6个零的浮点数转化为科学计数法。
浮点数的精度值最高可达17位小数,但是在算术计算中远不如整数精确。列如:0.1和0.2得到的不是0.3,而是0.3000000004。由于这种微小的舍入错误,导致很难测试特定的浮点值。比如下面的例子:
if(a+b==0.3){ //别这么干
console.log("YOU GOT 0.3")
}
这里检测两个数值之和是否等于0.3,如果两个数值分别是0.05和0.25,或者0.15和0.15,那没问题。但如果是0.1和0.2。如前所述。测试将失败,因此永远不要测试某个特定的浮点值
注意:之所以存在这种错误,是因为使用了IEEE 754数值。这种错误并非JavaScript所独有,其他使用相同格式的语言也有同样的问题
值的范围
由于内存的限制,ECMAScript并不支持表示世界上所有的数值。ECMAScript可以表示的最小的数值保存在Number.MIN_VALUE中。这个值在多数浏览器中是5e-324;可以表示的最大的数值保存在Number.MAX_VALUE中,这个值在多数浏览器中是1.7976931348623157e+308。如果某个计算得到的数值超出了JavaScript可以保存的范围,那么他会显示Infinity(无穷大)值。任何无法表示的负数以-Infinity表示。
如果计算结果范围-Infinity或者+Infinity,则该值将不能进一步用于任何计算,要确定一个数是不是无穷大。可以用isFinite()函数,如下所示:
let result=Number.MAX_VALUE + Number.MAX_VALUE;
console.log(isFinite(result)); // false
虽然超出了有限数计算范围的计算并不多见,但是总归还是有可能的。因此在计算非常大或者非常小的数值的时候,有必要监测一下计算结果是否超出了范围。
注意,Number.NEGATIVE_INFINITY和Number.POSITIVE_INFINITY也可以获得正负infinity
Nan
有一个特殊的数值叫做Nan,意思是"不是数值",用于表示本来要返回数值的操作失败了(而不是抛出错误)。比如,用0除以任何数值在其他语言中都会导致错误。从而中断代码执行,但是在ECMAScript中,0,+0,-0相除都会返回Nan;
console.log(0/0) //Nan
console.log(-0/+0) //Nan
如果分子是非0值,分母是有符号0和无符号0,则会返回Infinity或者-Infinity;
console.log(5/0); //Infinity
console.log(5/-0); //-Infinity
Nan有几个独特的属性,首先,任何涉及Nan的操作始终返回Nan。在连续多步计算这可能是个问题。其次,Nan不等于Nan在内的任何值,列如,下列操作会返回false
console.log(Nan==Nan) //false
为此,ECMAScript提供了isNan()这个函数,该函数接受一个参数,可以是任何数据类型,然后尝试判断这个参数是否不是数值。某些非数值的值可以直接转化为数值。如字符串10或者Bool值。任何不能转换为数值的值都会导致这个函数范围true。
console.log(isNan(Nan)) //true
console.log(isNaN(10)) //false, 10是数值
console.log(isNaN("10")) //false,可以转换为数值10
console.log(isNan("blue")) //true不可以转换为数值
console.log(isNan(true)) //false,不可以转为数值1
上述的例子测试了5个值,但是isNaN()可以用于测试对象,此时,首先会调用对象的valueOf()方法,然后再确定返回的值是否可以变成数值,如果不能,在调用对象的toString方法。并测试其返回值,这通常是ECMAScript内置函数的操作符的工作方法。
数值转换
有三个函数可以将非数值转换为数值:Number()、parseInt()和parseFloat().。Number是转型函数,可以用于任何数据类型。后两个函数主要将字符串转换为数值。对于同样的参数,这三个函数执行的操作也不同:
Number()函数基于如下的规则进行转换:
-
布尔值:true直接返回1,false直接返回0
-
数值:直接返回
-
null,返回0
-
undefined:返回Nan
-
字符串,应用以下规则。
-
如果字符串包含数值字符,包含数值字符前面带+,-号的情况,则转换一个10进制的值,因此Number("1")返回1,Number("123")返回123,Number("011")返回11(忽略前面的0)
-
如果字符串包含有效的浮点格式如1.1,则会转换为相应的浮点值。
-
如果字符串包含有效的16进制格式如“0xf”,则会转换为相应的16进制对应的10进制值。
-
如果是空字符串,就返回0
-
如果不是上面任意一种情况,就返回Nan
-
对象:调用Valueof。如果不是返回数字,那么就调用toString()