第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 子句,而与 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语句

4.14 循环语句

4.15 while语句

4.16 do-while语句

4.17 for语句

4.18 for in语句

4.19 for each in语句

4.20 break语句

4.21 continue语句

4.22 通过标签跳转

4.23 return语句

4.24 异常

4.25 其他

4.26 注释

4.27 表达式

4.28 运算符

4.29 表达式求值

4.30 运算符的优先级以及结合律

4.31 算术运算符

4.32 字符串连接运算符

4.33 相等运算符

4.34 比较运算符

4.35 in运算符

4.36 instanceof运算符

4.37 逻辑运算符

4.38 位运算符

4.39 赋值运算符

4.40 算术赋值运算符

4.41 条件运算符(三目运算符)

4.42 typeof运算符

4.43 new运算符

4.44 delete运算符

4.45 void运算符

4.46 逗号(,)运算符

4.47 点运算符和中括号运算符

4.48 函数调用运算符

4.49 运算符使用以及数据类型转换中需要注意的地方

相关