1. 标识符
标识符是指变量,函数,属性的名字,或者函数的参数。ECMAScript中的一切都区分大小写。标识符的命名规则如下:
- 第一个字符必须是一个字母,下划线或者美元符
- 其他字符可以是字母,下划线,美元符或数字
按照惯例,ECMAScript标识符使用驼峰法命名。
2. 变量
ECMAScript中的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个用于保存值的占位符而已。定义变量使用var操作符,后跟一个变量名:
var message = "hi"
message = 100 //有效,但不推荐
在上面的例子中,变量开始保存了一个字符串"hi",随后被数字100取代,虽然这种操作在ECMAScript中合法,但是我们不推荐这样修改变量所保存的值的类型。
有一点需要注意,使用var定义的变量将成为定义该变量的作用域中的局部变量。也就是说,如果在函数中使用var定义一个变量,那么这个变量在函数退出后就会被销毁。例如:
function test(){
var message = "hi"; //局部变量
}
test();
alert(message); //错误!
不过,上面例子中当定义message变量省略var操作符时,创建的就是一个全局变量。当test()方法被调用后,message变量就有了定义,就可以在函数外部的任何地方被访问到。
虽然省略var操作符可以定义全局变量,但这样不是我们推荐的做法。因为在局部作用域中定义的全局变量很难维护,而且如果有意的忽略var操作符,也会由于相应变量不会马上有定义而导致不必要的混乱。另外,给未经声明的变量赋值在严格模式下会抛出ReferenceError错误。
3. 数据类型
ECMAScript中有5种简单数据类型,分别为Undefined,Null,Boolean,Number和String。另外还有一种复杂类型:Object,Object本质上是由一组无序的名值对组成的。
3.1 typeof操作符
由于ECMAScript是松散类型的,因此需要一种手段来检测变量的数据类型。typeof就是负责返回变量数据类型的操作符。对一个值使用typeof操作符可能返回以下某个字符串:
- "undefined":如果这个值未定义
- "boolean":如果这个值是布尔值
- "string":如果这个值是字符串
- "number":如果这个值是数值
- "object":如果这个值是对象或null
- "function":如果这个值是函数
从技术角度来看,函数在ECMAScript中是对象,不是一种数据类型。然而函数也的确有一些特殊的属性,因此通过typeof来区分函数和其他对象是有必要的。
下面是几个使用tyoeof操作符的例子:
var message = "hello, world";
alert(typeof message); //"string"
alert(typeof 95); //"number"
3.2 Undefined类型
Undefined类型只有一个值,即特殊的undefined。在使用var声明一个变量但未对其进行初始化时,这个变量的值就是undefined。例如:
var message;
alert(message == undefined); //true
如果我们使用undefined字面量初始化一个变量,经过比较我们发现它仍然是Undefined类型的变量。
var message = undefined;
alert(message == undefined); //true
这个例子使用undefined显示的初始化了变量message,然而我们没有必要这样做,因为未经初始化的变量的默认值就为undefined。
不过,包含undefined值的变量与未经定义的变量还是不同的。看看下面这个例子:
var message;
alert(message); //"undefined"
alert(age); //产生错误!
运行以上代码,由于传递给alert函数的是尚未声明的变量age,因此会产生错误。对于未声明的变量,只能执行一项操作,那就是使用typeof检测其数据类型。对未声明的变量执行typeof操作,也会返回"undefined"。
3.3 Null类型
Null类型是第二个只有一个值的数据类型,这个特殊的值是null。从逻辑角度来看,null值表示一个空对象指针,因此使用typeof检测null值会返回"object"。下面是一个例子:
var car = null;
alert(typeof car); //"object"
如果定义的变量准备在将来用于保存对象,那么最好在开始就将其显示的声明为null。这样一来,只要检查null值就可以知道相应的变量是否已经保存了一个对象的引用,如下面的例子:
if (car != null){
//对car执行操作
}
事实上,undefined值是派生自null值的,因此EMAC-262规定对于他们的相等性测试要返回true:
alert(null == undefined); //true
尽管null和undefined之间有这样的关系,但他们的用途完全不同。如前所述,无论在任何情况下,都不应该将变量值显示的设置为undefined。然而如果确定某个变量用于保存对象,那么就应该明确的让该变量保存null值。
3.4 Boolean类型
Boolean类型是ECMAScript中使用的最多的类型,该类型只有两个字面值:true和false。下面是为变量赋Boolean类型值的例子:
var found = true;
var lost = false;
虽然Boolean类型的字面值只有两个, 但ECMAScript中所有类型的值都有与这两个Boolean值等价的值。要将一个值转换为Boolean类型的值,可以使用Boolean()函数。一个例子如下:
var message = "hello, world";
var messageAsBoolean = Boolean(message); //true
可以对任何类型的数据调用Boolean()函数从而将其转换为Boolean类型的值。至于返回的值是true还是false,取决于转换值的数据类型和具体值。 下面是具体的各个类型的转换规则:
- Boolean: true=>true;false=>false
- String:任何非空字符串=>true;""=>false
- Number:任何非零数字=>true;0和NaN=>false
- Object:任何对象=>true;null=>false
- Undefined:undefined=>false
上面的规则对理解流控制语句(如if语句)自动执行Boolean转换非常重要,请看下面的例子:
var message = "hello, world";
if (message){
alert("Value is true!");
}
3.5 Number类型
Number类型使用IEEE754格式来表示整数和浮点数。整数值定义如下:
var num1 = 55; //十进制数55
var num2 = 070; //八进制数56。以0开头,后面是0-7序列的数会解析为八进制数。
var num3 = 079; //十进制数79。无效的八进制数,忽略首位0,解析为十进制数。
var num4 = 0xA; //十六进制数10
在进行算数计算时,所有八进制和十六进制表示的数值最终都将被转换为十进制数。
下面时浮点数值定义的例子:
var num1 = 0.1;
var num2 = .1; //解析为0.1。有效,但不推荐
由于保存浮点数值所需要的内存空间是保存整数值的两倍,因此ECMAScript会不失时机的将浮点数值转换为整数值。
var num1 = 1.; //解析为整数值1
var num2 = 10.0; //解析为整数值10
浮点数值的最高精度是17位小数,但在算数计算时其精确度远远不如整数。例如,0.1加0.2的值不是0.3,而是0.300000000004。这个小小的舍入误差会导致无法测试特定的浮点数值。例如:
if (a + b == 0.3){ //不要做这样的测试!
alert("You got 0.3!");
}
3.5.1 数值范围
ECMAScript能够表示的最小数值保存在Number.MIN_VALUE中,能够表示的最大数值保存在Number.MAX_VALUE中。如果计算的结果超出了JavaScript能够表示的数值范围,那么这个数值将会被转换成特殊的Infinity值。具体来说,如果这个数值是负数,那么会被转换成Number.NEGTIVE_INFINITY,如果时正数,则被转换成Number.POSITIVE_INFINITY。
如果某次计算返回了返回了Infinity值,那么该值无法参与下一次计算。要想确定一个数值是不是有穷的,可以使用isFinite()函数,例如:
var result = Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(result)); //false
3.5.2 NaN
NaN,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数但是未返回数值的情况(这样就不会抛出错误了)。例如,在ECMAScript中,任何数值除以0都会返回NaN,而不会直接抛出错误从而影响代码执行。
NaN具有两个非同寻常的特点,首先,任何设计NaN的操作都会返回NaN;其次,NaN与任何值都不相等,包括NaN本身。
alert(NaN == NaN); //false
针对以上特点,ECMAScript定义了isNaN()函数,来判断一个参数是否”不是数值“。isNaN()函数会尝试将传入参数转换为数值,如果该参数无法转换为数值,那么返回true。
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false
alert(isNaN("10")); //false。能够被转换为数值10
alert(isNaN("blue")); //true
alert(isNaN(true)); //false。能够被转换为数值1
isNaN()函数也同样是用于对象。在基于对象调用isNaN()函数时,会首先调用对象的valueOf()方法,然后确定该方法的返回值是否能被转换为数值。如果不能,则基于这个返回值再调用toString()方法,再测试返回值。这也是ECMAScript中内置函数和操作符的一般执行流程。
3.5.3 数值转换
有三个函数可以把非数值转换为数值:Number(),parseInt()和parseFloat()。Number()函数接受任何类型的参数,而parseInt()和parseFloat()函数则只能接受一个字符串。
Number()函数的转换规则如下:
- Boolean类型:true => 1;false => 0
- Number类型:直接返回
- Null类型:返回0
- Undefined类型:返回NaN
- String类型:字符串时合法数值(包括正负数,十六进制,浮点数)=> 返回十进制数值或浮点数;"" => 0;包含任何非法字符 => NaN
- Object类型:首先调用valueOf()方法,得到的返回值按照前面的规则进行转换。如果转换结果为NaN,继续调用对象的toString()方法,按照前面的规则对返回值进行转换。
下面是一些例子:
var num1 = Number("hello, world"); //NaN
var num2 = Number(""); //0
var num3 = Number("0011"); //11
var num4 = Number(true); //1
一元加操作符的操作与Number函数相同。
由于Number()函数再转换字符串时比较复杂且不够合理,因此在处理整数时更常用的时parseInt()函数。parseInt()函数会忽略字符串前面的空格,直到找到第一个非空字符,如果该字符合法则继续检查下去,直到碰到第一个非法字符。例如" 1"会被转换为数值1,"1234blue"会被转换为1234。下面是一些例子:
var num1 = parseInt("1234blue"); //1234
var num2 = parseInt(""); //NaN
var num2 = parseInt("a1234"); //NaN
var num3 = parseInt("0xA"); //10
var num4 = parseInt(22.5); //22
var num5 = parseInt("070"); //不确定。ECMAScript3会将其看作八进制数,因此结果为56。而其他版本则直接忽略0,因此转换为70
为了消除像parseInt("070")这样的不确定因素,可以在使用这个函数时指定第二个参数:转换时使用的基数(即使用多少进制)。例如:
var num1 = parseInt("0xAF", 16); //175
var num2 = parseInt("AF", 16); //175
var num3 = parseInt("AF"); //NaN
不指定基数意味着让parseInt()函数自行决定如何解析输入的字符串,因此为了避免错误的解析,我们建议始终显示的指定基数。
parseFloat()函数与parseInt()函数类似,也是从第一个字符开始解析,直到遇到第一个非法字符为止。另外,parseFloat()函数始终会忽略字符串中的前导零。由于parseFloat()函数只解析十进制值,因此任何十六进制数都会被转换为0,并且它没有用第二个参数指定基数的用法。最后,如果一个字符串包含的是一个可解析为整数的数(没有小数点,或小数点后面都为零),那么parseFloat()会返回整数。下面是一些parsseFloat()函数的例子:
var num1 = parseFloat("1234blue"); //1234(整数)
var num1 = parseFloat("0xA"); //0
var num1 = parseFloat("22.5"); //22.5
var num1 = parseFloat("22.34.5"); //22.34
var num1 = parseFloat("0908.5"); //908.5
var num1 = parseFloat("3.125e7"); //31250000