首页 > 代码库 > JS基础知识回顾:ECMAScript的语法(二)

JS基础知识回顾:ECMAScript的语法(二)

ECMAScript中有五种简单数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number、String

ECMAScript还有一种复杂数据类型——Object,Object本质上是由一组无序的名值对组成的。

ECMAScript不支持任何创建自定义类型的机制,而所有值最终都将是上述六种数据类型之一,由于ECMAScript的数据类型具有动态性,因此的确没有再定义其他数据类型的必要了。

 

监狱ECMAScript是松散类型的,因此需要有一种手段来检测给定变量的数据类型,typeof就是负责提供这方面消息的操作符。

typeof的操作数可以是变量也可以是数值字面量,typeof是一个操作符而不是函数,所以在使用时可以加圆括号也可以不加。

对一个值使用typeof操作符可能返回下列某个字符串:

"undifined"——该值未定义

"boolean"——该值为布尔型

"string"——该值为字符串

"number"——该值为数值

"object"——该值为对象或null(特殊值null被认为是一个空的对象引用)

"function"——该值为函数(Safari5以及之前的版本、Chrome7以及之前的版本对正则表达式调用typeof操作符时会返回"function",其他浏览器则返回"object")

从技术的角度来讲,函数在ECMAScript中是对象,不是一种数据类型,然而函数也确实有一些特殊的属性,因此通过typeof操作符来区分函数和其他对象是很有必要的。

 

Undefined类型只有一个值,即特殊的undefined,在使用var声明变量但未对其进行初始化时,这个变量的值就是undefined。

一般而言,不需要显示地把一个变量设置为undefined,字面值undefined通常用于比较,在ECMA-262第三版之前并没有这个值,第三版引入它的目的是为了正式区分空对象指针与未经初始化的变量。

对于尚未声明过的变量只能执行一项操作,即使用typeof操作符检测其数据类型(对未经声明的变量调用delete不会导致错误,但并没有实际意义)。

对于尚未声明过的变量调用typeof操作符也会返回undefined值,尽管它和未初始化的值有本质上的区别。

即便未初始化的变量会自动被赋予undefined值,但显示地初始化变量仍然是明智的选择,如果能够做到这一点,那么当typeof操作符返回undifined值时,就能够知道该变量是未被声明,而并不是尚未初始化。

 

Null类型只有一个值,即特殊的null,从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof操作符检测null值时会返回object的原因。

如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他的值,这样一来,只要直接检查null值就可以知道相应的变量是否已经保存了一个对象的引用。

实际上undefined值是派生自null值的,因此ECMA-262中对于二者的相等性测试返回的是true,尽管有这样的关系,但它们的用途完全不同。

只要意在保存对象的变量还没有真正保存对象,就应该明确的让该变量保存null值,这样做不仅可以体现null作为空对象指针的惯例,也有助于进一步区分null和undefined。

 

Boolean类型是ECMAScript中用的最多的一种类型,该类型只有两个字面值:"true"和"false"。

Boolean类型的字面量是区分大小写的,并且ECMAScript中所有类型的值都有与这两个Boolean值等价的值。

要将一个值转换为其对应的Boolean值,可以调用转型函数Boolean(),各种数据类型及其对应的转换规则如下:

Boolean:true(返回true)、false(返回false)

String:任何非空字符串(返回true)、""(返回false)

Number:任何非零数值/包括无穷大(返回true)、0和NaN(返回false)

Object:任何对象(返回true)、null(返回false)

Undefined:n/a(返回true)、undefined(返回false)——n/a是not applicable的缩写,意思是“不适用”

这些转换规则对于理解控制流语句自动执行相应的Boolean转换非常重要,错误的使用一个对象而不是Boolean值就有可能彻底改变应用程序的流程。

 

Number类型使用了IEEE754格式来表示整数和浮点数值,为了支持各种数值类型,ECMA-262定义了不同的数值字面量格式:

十进制字面值(在进行算术运算时,所有以八进制和十六进制表示的数值最终都将被转换成十进制数值)

八进制字面值(第一位为0,后面为八进制数字序列0-7,若该序列超出八进制范围则将被省略前导零看做十进制整数,八进制整数在严格模式下无效,会导致支持的JavaScript引擎抛出错误)

十六进制字面值(前两位为0x,后跟十六进制数字0-9和A-F,字母大小写随意)

鉴于JavaScript保存数值的方式,可以保存正零(+0)和负零(-0),正零和负零被认为相等。

 

浮点数值当中必须包含一个小数点,并且小数点后面必须至少有一位数字,小数点前可以没有整数,但并不推荐这种写法。

由于保存浮点数值需要的内存空间是保存整数值的两倍,因此ECMAScript会不失时机的将浮点数值转换为整数值:小数点后无数字、浮点数本身表示的是整数。

也可以用科学计数法表示的浮点数值来表示极大或极小的数值,用科学计数法表示的数值等于e前面的数值乘以10的指数次幂,此处的e大小写随意。

浮点数值的最高精度是17位小数,但基于IEEE754数值的浮点计算普遍存在计算不准确的问题,所以永远不要测试某个特定的浮点数值。

 

由于内存的限制,ECMAScript并不能保存所有的数值。

ECMAScript能够表示的最小数值保存在Number.MIN_VALUE中,在大多数浏览器中这个值是5e-324。

ECMAScript能够表示的最大数值保存在Number.MAX_VALUE中,在大多数浏览器中这个值是1.7976931348623157e+308。

如果某次计算结果超出该范围,则那个数值将被自动转换成特殊的Infinity值,若为正数则返回Infinity(正无穷),若为负数则返回-Infinity(负无穷)。

访问Number.NEGATIVE_INFINITY和Number.POSITIVE_INFINITY可以得到-Infinity和Infinity。

如果想要确认某个数值是否在正负无穷的范围内,可以使用isFinite()函数,如果这个函数的参数在正负无穷之间会返回true。

 

NaN即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来应该返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。

在其他编程语言中,任何数值除以0都会导致错误,而在ECMAScript中会返回NaN,因而不会影响其他代码的执行。

任何涉及NaN的操作都会返回NaN,这个特点在多步计算中有可能导致问题,NaN与任何值都不相等,包括它本身。

针对以上特点ECMAScript定义了isNaN()函数,这个函数接受一个参数后将尝试将其转换为数值,如果无法转换为数值则会返回true:

alert(isNan(NaN));//true

alert(isNan(10));//false(数值10)

alert(isNan("10"));//false(可以被转换为数值10)

alert(isNan("blue"));//true(无法转换为数值)

alert(isNan(true));//false(可以被转换为数值1)

isNaN()同样适用于对象,在基于对象调用该函数时,会首先调用对象的valueof()方法,然后确定能否返回数值,若不能,将基于该返回值再次调用toString()方法,再测试返回值,这个过程也是ECMAScript中内置函数和操作符的一般执行流程。

 

有三个函数可以把非数值转换为数值:Number()、parseInt()、parseFloat()。

第一个函数可以用于任何数据类型,而另外两个函数则专门用于把字符串转换成数值。

Number()函数的转换规则如下(一元加操作符的操作与该函数相同):

如果是Boolean值,true和false将分别转换为1和0;

如果是数字值,只是简单的传入和返回;

如果是null值,返回0;

如果是undefined,返回NaN;

如果是字符串,遵循下列规则:

  如果字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值(忽略前导0)

  如果字符串中包含有效的浮点格式,则将其转换为浮点数值(忽略前导0)

  如果字符串中包含有效的十六进制格式,则将其转换为相同大小的十进制整数值

  如果字符串是空的,则将其转换为0

  如果字符串中包含除上述格式之外的字符,则将其转换为NaN

如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值,如果转换的结果是NaN,则调用对象的toString()方法,然后再次依照前面的规则转换返回的字符串值。

由于Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是parseInt()函数。

parseInt()函数在转换字符串时,更多的是看其是否符合数值模式,它会忽略字符串前面的空格,直到找到第一个非空格字符。

如果第一个字符不是数字字符或者负号,parseInt()就会返回NaN,如果第一个字符是数字字符则会继续解析直到遇到非数字字符位置。

var num1=parseInt("1234blue");//1234

var num2=parseInt("");//NaN

var num3=parseInt("0xA");//10(十六进制数)

var num4=parseInt("22.5");//22

var num5=parseInt("070");//56(八进制数)

var num6=parseInt("70");//70(十进制数)

var num7=parseInt("0xf");//15(十六进制数)

尽管parseInt()函数能够识别出各种整数格式,但是ECMAScript3和5在处理八进制数时存在分歧。

在ECMAScript3中"070"被当成八进制字面量,而在ECMAScript5已经不具备解析八进制数值的能力,因而会忽略前导0而将其直接转换为十进制数值。

为了消除在使用parseInt()函数时可能导致的困惑,可以为其提供第二个参数:转换时使用的基数(即多少进制),来保证得到正确的结果。

parseFloat()函数时从第一个字符开始解析直到遇到一个无效的浮点数字字符为止,也就是说字符串中的第一个小数点有效但第二个小数点就无效了。

parseFloat()函数始终都会忽略前导0,它可以识别浮点数值格式和十进制整数格式,但十六进制格式的字符串始终会被转换为0。

parseFloat()函数只解析十进制数值,因此它没有用第二个参数指定基数的用法,如果字符串包含的是一个可以解析为整数的值也会返回整数。

var num1=parseFloat("1234blue");//1234(整数)

var num2=parseFloat("0xA");//0(无法解析十六进制数,所有十六进制数均会被解析为0)

var num3=parseFloat("22.5");//22.5

var num4=parseFloat("22.34.5");//22.34(第二个小数点无效)

var num5=parseFloat("0908.5");//908.5(始终忽略前导0)

var num6=parseFloat("3.125e7");//31250000(科学计数法)

 

String类型用于表示由零个或多个16位Unicode字符组成的字符序列,即字符串。

字符串可以由单引号或双引号表示,在PHP中单引号和双引号会影响对字符串的解释方式不同,在ECMAScript中没有什么区别,但是要注意左右引号必须匹配。

String数据类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其他用途的字符。

\n 换行  \t 制表  \b 空格  \r 回车  \f 进纸  \\ 斜杠  \‘ 单引号  \" 双引号

\xnn 以十六进制代码nn表示的一个字符(其中n为0-F),例如:\x41表示"A"

\unnnn 以十六进制代码nnnn表示的一个Unicode字符(其中n为0-F),例如:\u03a3表示希腊字符∑

这些字符字面量可以出现在字符串中的任意位置,而且也将被作为一个字符来解析。

任何字符串的长度都可以通过访问其length属性取得,这个属性返回的字符数包括16位字符的数目,如果字符串中包含双字节字符,那么length属性可能不会精确地返回字符串中的字符数目。

ECMAScript中的字符串时不可变的,也就是说,字符串一旦创建,它们的值就不能改变。

要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量。

这个过程是在后台发生的,而这也是某些旧版本的浏览器中拼接字符串时速度很慢的原因,不过他们的高版本已经解决了这个问题。

 

要把一个值转换为字符串有两种方式:toString()方法和String()函数。

数值、布尔值、对象和字符串值都有toString()方法,而null和undefined没有这个方法。

多数情况下调用toString()方法不必传递参数,但是也可以传递输出数值的基数作为参数,这个参数可以是任意有效的进制格式,默认没有参数的输出值与指定基数10时的输出值相同。

在不知道要转换的值是不是null或undefined的情况下,可以使用String()函数,这个函数能够将任何类型的值转换为字符串。

如果该值有toString()方法,则调用该方法(没有参数)并返回相应结果;如果该值为null,则返回"null";如果该值是undefined,则返回"undefined"。

要把某个值转换为字符串,可以使用加好操作符把它与一个字符串("")加在一起。

 

ECMAScript中的对象其实就是一组数据和功能的集合。

对象可以通过执行new操作符后跟要创建的对象类型的名称来创建,例如:var o=new Object();

创建Object类型的实例并为其添加属性和方法,就可以创建自定义对象。

在ECMAScript中如果不给构造函数传递参数,则可以省略后面的那一对圆括号,但是并不推荐这么做。

在ECMAScript中Object类型是所有它的实例的基础,Object类型所具有的任何属性和方法也同样存在于更具体的对象当中:

constructor:保存着用于创建当前对象的函数,对于上面的例子而言就是构造函数Object();

hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中是否存在,其中作为参数的属性名(propertyName)必须以字符串的形式指定;

isPrototypeOf(Object):用于检查传入的对象是否是传入对象的原型;

propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句来枚举,其中作为参数的属性名(propertyName)必须以字符串的形式指定;

toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应;

toString():返回对象的字符串表示;

valueOf():返回对象的字符串、数值或布尔值表示,通常与toString()方法的返回值相同。

 

就技术的角度而言,ECMA-262对象的行为不一定适用于JavaScript中的其他对象。

浏览器环境中的对象,比如BOM和DOM中的对象都属于宿主对象,因为他们是由宿主实现提供和定义的。

ECMA-262不负责定义宿主对象,因此宿主对象可能会也可能不会继承Object。