第4章 语句表达式和运算符
尽管 JavaScript 在语法结构上,有不少地方和 Java 类似,但它有一些自己独有的语句。同样地,在 JavaScript 中,有很多和 Java 相似的运算符和表达式。不过,因为隐式的数据类型转换在 JavaScript中非常普遍,所以与 Java 相比,在使用这一语言的过程中还有其他一些必须注意的地方。
4.1 表达式和语句的构成
JavaScript 的源代码本质上是一个语句的集合。语句是由语句和表达式所构成的。表达式则由表达式和运算符所构成。这种在自身的定义中递归地使用自身的定义方式,在程序设计语言中相当常见。
有人可能会觉得,这种使用了自身的定义方式即使在经过了无限次循环之后,也无法真正地定义出一个概念。不过事实上,语句和表达式都具有不需要用到自身定义的定义方式。因此,这种递归的定义是不会无限循环下去的。对于语句来说,最终都可以被分解为保留字、表达式与符号(括号或是分号等)。也就是说,即使在一条语句中包含其他语句,只要对这条被包含的语句继续进行分解,最终都会到达仅包含保留字、表达式与符号的状态。对于表达式来说,虽然也能在一句表达式中包含其他的表达式,不过只要对所包含的表达式继续进行分解,最终总是能达到仅包含标识符(变量名或是函数名)、字面量(即直接写出其值的数值或是字符串)与运算符(符号或是保留字)的状态。
4.2 保留字
JavaScript 的保留字
关键字
今后的保留字
4.3 标识符
标识符是开发者在程序中所定义的单词,例如变量名或是函数名。虽说标识符中可以使用的字符是有所限制的,不过只要不与保留字中的单词重复就没有问题,所以实际上可以生成无限多的标识符。其具体的命名规则如下。
- 必须是除保留字以外的单词。
- 必须是除 true、false、null 以外的单词。
- 必须是以 Unicode 的(非空)字符开始,之后接有Unicode 字符或是数字的单词。
- 单词的长度并无限制。
不能使用和保留字相同的单词作为标识符。例如,如果有一个函数被命名为 do,那么就会引起 SyntaxError。不过,如果仅仅是在标识符的字符中包含了保留字就没有问题,例如 doit 这样的函数名就是合法的。
var do = 7;
console.log("输出do的值:");
console.log(do);
var doit = 8;
console.log("输出doit的值:");
console.log(doit);
true、false 和 null 这三个单词是字面量,不能被用作标识符。就好比数值字面量 1 或是字符串字面量 "abc" 不能被用作标识符一样,这三个字面量也不能被用作标识符。
JavaScript 中的标识符都是以 Unicode 字符所组成的单词。Unicode 字符中包含了日文字符(日文汉字以及平片假名),所以从语法上来说,是可以使用日文作为变量名以及函数名的。不过,由于一些历史原因以及习惯用法,并不推荐使用日文作为标识符。在实际的编程过程中应当遵循以下的规则,即应该使用以英文字符(大写或是小写的英文字符)、_(下划线字符)或是
$
(美元字符)开始,之后接有英文字符、_
、$
、数字(0 至 9)的单词。
var hzh = 'hzh';
var HZH = 'HZH';
var _hzh = '_hzh';
var $hzh = '$hzh';
var hzh_ = 'hzh_';
var hzh$ = 'hzh$';
var hzh6 = 'hzh6';
var HZH_ = 'HZH_';
var HZH$ = 'HZH$';
var HZH9 = 'HZH9';
var _hzh_ = '_hzh_';
var _hzh$ = '_hzh$';
var _hzh12 = '_hzh12';
var $hzh_ = '$hzh_';
var $hzh$ = '$hzh$';
var $hzh15 = '$hzh15';
var hzh_$ = 'hzh_$';
var hzh_0 = 'hzh_0';
var hzh$0 = 'hzh$0';
var HZH_$ = 'HZH_$';
var HZH_0 = 'HZH_0';
var HZH$0 = 'HZH$0';
var _hzh_$ = '_hzh_$';
var _hzh_0 = '_hzh_0';
var _hzh$0 = '_hzh$0';
var $hzh_$ = '$hzh_$';
var $hzh_0 = '$hzh_0';
var $hzh$0 = '$hzh$0';
console.log("看看能不能把它们打印出来:");
console.log(hzh, HZH, _hzh, $hzh, hzh_, hzh$, hzh6);
console.log(HZH_, HZH$, HZH9, _hzh_, _hzh$, _hzh12);
console.log($hzh_, $hzh$, $hzh15, hzh_$, hzh_0);
console.log(hzh$0, HZH_$, HZH_0, HZH$0, _hzh_$);
console.log(_hzh_0, _hzh$0, $hzh_$, $hzh_0, $hzh$0);
习惯上,以下划线(_)开始的标识符会被作为“内部标识符”来使用。又因为在 prototype.js 中,getElementById 函数的别名被记为了
$
,所以一些常用名称的别名常常会使用以美元符号($)开始的标识符。
4.4 字面量
字面量(literal)指的是,在代码中写下这些值之后,将会在运行时直接使用这些值的字面含义。有读者也许会觉得,在代码中书写的值自然会在运行时按原样表达该值,不过事实上并非如此,请看下面的代码:
// 字符串字面量"bar" 的例子
var foo = "bar";
根据语法规则,代码中的 var 这个词的含义是变量的声明,因此,在运行中 var 并不会被识别为一个内容为 var 的单词。类似地,foo 这个词在运行时也不会被识别为一个内容为 foo 的单词,而仅被认为是变量 foo 所表示的值。而即使把代码中所有的 foo 都改写为 foo2 也不会改变运行结果,通过这一事实也能进一步理解该规则。
另一方面,"bar" 是一个字符串字面量,所以 bar 这一单词在运行过程中的含义就是 bar 这一字符序列而已。
数值字面量的情况就更加容易理解了。在下面的代码中写有两个数值 0。val0 中的 0 是其变量名的一部分,并不具有数值0 的含义。这个 0 已经失去了可以进行算术运算的性质,仅仅是一个符号。
另一方面,右侧的字面量 0 则具有数值的含义。
// 数值字面量0 的例子
var val0 = 0;
【评】如果设置一个变量为var hzh = '黄子涵'
,那么变量hzh表示的‘黄子涵’这个字符串,改变标识符也改变不了字面量的意思。
字面量
4.5 语句
在程序设计语言中,语句(statement)的定义可以由该语言经过明确定义的语法(syntax)规则得到,并且可以在运行程序时执行(execute)语句。换一种角度来说的话,所谓运行一个程序,指的就是执行程序中一条条的语句。
虽然说,源代码中的语句并不一定是和运行中的每一步一一对应的,不过考虑到程序在运行时确实是在逐一执行语句,所以在概念上并不矛盾。
在 JavaScript 中,语句之间使用分号分隔。严格来说,分号只是一部分语句的结尾。例如对于表达式语句,其结尾必须使用分号;而对于复合语句,其结尾则是不需要分号。对于这方面的规则,JavaScript 和 Java 基本相同,不过 JavaScript 对于分号的使用限制更为宽松。例如在 JavaScript 中,换行时所省略的分号将被自动补全。
4.6 代码块(复合语句)
代码块是在大括号({})中所写的语句,以此将多条语句的集合视为一条语句来使用。这样一来,从语法上来说,代码中所有能够书写语句的地方都可以书写多条语句。
值得注意的是,JavaScript(准确地说是 ECMAScript)的代码块中的变量并不存在块级作用域这样的概念。
4.7 变量声明语句
// 变量声明语句的格式为,在关键字 var 之后跟上所需的变量名。
var hzh1;
// 在多个变量名之间使用逗号(,)分隔的话,就能够同时声明多个变量。
var hzh2, hzh3;
// 而使用 = 运算符,就可以在声明的同时对变量进行初始化。
var hzh4 = '黄子涵', hzh5 = '是帅哥!'
4.8 函数声明语句
JavaScript 中的函数声明语句,和 Java 中方法的定义语句在语法上是基本相同的,不同之处在于,函数声明语句并不是以返回值类型开始,而是使用了关键字 function,并且在JavaScript 中不用参数指定类型。
尽管在 ECMAScript 标准中,函数声明语句并没有被视为语句的一种。
函数名和参数的位置上所书写的是标识符。参数的数量没有限制,所以即使一个参数也没有关系。大括号中的是函数体,里面可以书写多条语句。
4.9 表达式语句
Script 不同于 Java 那样,Java 只有一部分的表达式能够被作为语句使用,而在 JavaScript 中,所有的表达式都可以被视为一条表达式语句。不过很可惜,JavaScript 的这一特性并不是一个优点。
// 虽然没有意义,但是语法上并没有错误的代码
// 在相等运算符(==)的表达式从语法上来说属于是表达式语句
var hzh1;
hzh1 == 0; // 一条表达式语句(但是没有任何的实际效果)
// 执行上面的表达式语句并不会有任何效果。
// 又因为不会引起语法错误,所以即使不小心把 == 错写成了 = 也不容易被发现
// 而在 Java 中,这类没有意义的表达式语句将会引起编译错误。
// 能够很容易地发现问题,反而是一种优点
var hzh2;
hzh2 = '黄子涵'; // 赋值表达式的表达式语句
console.log(hzh2); // 函数调用表达式的表达式语句
4.10 空语句
仅含有分号的语句就是空语句。仅在一部分场合下空语句才有其使用价值。
4.11 控制语句
有一类语法规则被称为控制语句。控制语句包括条件分支、循环、跳转(包括异常处理)这 3 类。如果没有这样的控制语句,JavaScript 在理论上,是按照源代码上所写的代码顺序从上至下地执行。这种执行方式被称为“顺序执行”。
有了控制语句之后,就可以实现顺序执行以外的代码执行方式。
4.12 if-else语句
if 语句和 if-else 语句的语法结构如下。其中的条件表达式和语句不能省略。
// if 语句的语法
if ( 条件表达式 )
语句
// if-else 语句的语法
if ( 条件表达式 )
语句
else
语句
与 if 对应的条件表达式及语句统称为 if 子句,而与 else 对应的条件表达式与语句则统称为 else 子句。可以把 if 表达式看作 if-else 表达式省略了 else 子句的特殊情况。
在条件表达式的位置所写的式子,将被求值并转换为布尔型。这一隐式的数据类型转换常常会带来各种各样的错误。
// if-else语句的例子
var hzh1 = 0;
var hzh2 = 0;
var hzh3 = 0;
if(hzh1 == 0) {
console.log("if分句");
}
else {
console.log("else分句");
}
console.log("************************************************");
// 嵌套if-else语句
if(hzh2 == 0) {
if(hzh3 == 0) {
console.log("hzh2==0 and hzh3==0");
}
else {
console.log("hzh2==0 and hzh3!=0");
}
}
4.13 switch-case语句
// switch-case 语句是一种语法结构与 if-else 有所不同的条件分支判断语句
// switch-case 语句的语法
switch ( 语句 ) {
case 表达式 1:
语句
语句
......
case 表达式 2:
语句
语句
......
case 表达式 N:
语句
语句
......
default:
语句
语句
......
}
根据习惯,“case 表达式:”部分被称为 case 标签,而“default:”部分被称为 default 标签。从语法规则的角度来看,它们与 if-else 语句中子句的功能不同,起到的是跳转目标的作用。在 switch 语句中可以书写多个 case 标签,而 default 标签则只能使用 1 次。此外,default 标签是可以省略的。
尽管 JavaScript 中的 switch 语句在语法结构上与 Java 的相同,但它们在实际的语法规则上却有着一些细微的差异。在 JavaScript 中,switch 的括号内可以写任意类型的表达式,case 标签中也可以写任意的表达式。与之相对应地,在 Java 的 case 标签中,则只能使用在编译时就能够获得结果的常量表达
式。
switch 语句会把其在 switch 之后的括号内的表达式,与 case 标签中所写的各个表达式,依次通过相等运算符(
===
)进行比较。为了避免用词混淆,这里将前者称为 switch 表达式,而将后者称为 case 表达式。===
运算符是不会进行数据类型转换的相等运算符。switch 语句首先对 switch 表达式进行求值,之后依次对 case 表达式从上往下求值,并将其结果与 switch 表达式的求值结果进行等值比较(===
)。如果值相等,则跳转至该 case 标签处。如果与所有的 case 表达式都不等值,则跳转至 default 标签处。