# 语言基础

# 1、语法

# 1.1 区分大小写

无论是变量、函数名还是操作符,都区分大小写。

# 1.2 标识符

所谓标识符,就是变量、函数、属性或函数参数的名称。标识符可以由一或多个下列字符组成:

  • 第一个字符必须是一个字母、下划线(_)或美元符号($);
  • 剩下的其他字符可以是字母、下划线、美元符号或数字。

标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符,如 À 和 Æ(但不推荐使用)

# 1.3 注释

分为单行注释和多行注释:

// 单行注释

/* 多行
注释 */
1
2
3
4

# 1.4 严格模式

ECMAScript 5 增加了严格模式(strict mode)的概念。

主要用于对不安全的活动将抛出错误。要对整个脚本启用严格模式,在脚本开头加上这一行:

"use strict";
1

也可以作用在函数块:

function test() {
  "user strict";
  // 函数体
}
1
2
3
4

# 1.5 语句

ECMAScript 中的语句以分号结尾。省略分号意味着由解析器确定语句在哪里结尾。

let sum = a + b // 没有分号也有效,但不推荐
let diff = a - b; // 加分号有效,推荐
1
2

# 2、关键字与保留字

ECMA-262 描述了一组保留的关键字,这些关键字有特殊用途

ECMA-262 第 6 版规定的所有关键字如下:

  • break do in typeof
  • case else instanceof var
  • catch export new void
  • class extends return while
  • const finally super with
  • continue for switch yield
  • debugger function this
  • default if throw
  • delete import try

以下是 ECMA-262 第 6 版为将来保留的所有词汇。

始终保留:

  • enum

严格模式下保留:

  • implements package public
  • interface protected static
  • let private

模块代码中保留:

  • await

# 3、变量

ECMAScript 变量是松散类型的,意思是变量可以用于保存任何类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。

有 3 个关键字可以声明变量:varconstlet。其中,var 在 ECMAScript 的所有版本中都可以使用,而 const 和 let 只能在 ECMAScript 6 及更晚的版本中使用。

详解见js变量详解

# 4、数据类型

ECMAScript 有 7 种简单数据类型(也称为原始类型):UndefinedNullBooleanNumberBigIntStringSymbol。还有一种复杂数据类型叫 Object(对象)。Object 是一种无序名值对的集合。

详解见js数据类型

# 5、操作符

ECMA-262 描述了一组可用于操作数据值的操作符,包括数学操作符(如加、减)、位操作符、关系操作符和相等操作符等。

在应用给对象时,操作符通常会调用 valueOf() 和/或 toString() 方法来取得可以计算的值。

# 5.1 一元运算符

(1)递增/递减操作符

// 递增
let age = 29; 
++age; 

// 递减
let age = 29; 
--age;
1
2
3
4
5
6
7

(2)一元加和减

// 一元加
let num = 25; 
num = +num; 
console.log(num); // 25

// 一元减
let num = 25; 
num = -num; 
console.log(num); // -25
1
2
3
4
5
6
7
8
9

# 5.2 位操作符

位操作符操作内存中表示数据的比特(位)

ECMAScript 中的所有数值都以 IEEE 754 64 位格式存储,但位操作并不直接应用到 64 位表示,而是先把值转换为 32 位整数,再进行位操作,之后再把结果转换为 64 位。

有符号整数使用 32 位的前 31 位表示整数值。第 32 位表示数值的符号,如 0 表示正,1 表示负。这一位称为符号位(sign bit),它的值决定了数值其余部分的格式。

正值以真正的二进制格式存储,负值以一种称为二补数(或补码)的二进制编码存储。

  • (1) 确定绝对值的二进制表示(如,对于18,先确定 18 的二进制表示);
  • (2) 找到数值的一补数(或反码),换句话说,就是每个 0 都变成 1,每个 1 都变成 0;
  • (3) 给结果加 1

(1)按位非

按位非操作符用波浪符(~)表示,它的作用是返回数值的一补数。

let num1 = 25; // 二进制 00000000000000000000000000011001 
let num2 = ~num1; // 二进制 11111111111111111111111111100110 
console.log(num2); // -26 
1
2
3

就像执行如下操作的结果一样:

let num1 = 25; 
let num2 = -num1 - 1; 
console.log(num2); // "-26"
1
2
3

实际上,尽管两者返回的结果一样,但位操作的速度快得多。这是因为位操作是在数值的底层表示上完成的。

(2)按位与

按位与操作符用和号(&)表示,有两个操作数。

本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作。

第一个数值的位 第二个数值的位 结 果
1 1 1
1 0 0
0 1 0
0 0 0

按位与操作在两个位都是 1 时返回 1,在任何一位是 0 时返回 0。

let result = 25 & 3; 
console.log(result); // 1
1
2

25 和 3 的按位与操作的结果是 1。为什么呢?看下面的二进制计算过程:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001 
  3 = 0000 0000 0000 0000 0000 0000 0000 0011 
--------------------------------------------- 
AND = 0000 0000 0000 0000 0000 0000 0000 0001
1
2
3
4

(3)按位或

按位或操作符用管道符(|)表示,同样有两个操作数。按位或遵循如下真值表:

第一个数值的位 第二个数值的位 结 果
1 1 1
1 0 1
0 1 1
0 0 0

按位或操作在至少一位是 1 时返回 1,两位都是 0 时返回 0。

仍然用按位与的示例,如果对 25 和 3 执行按位或,代码如下所示:

let result = 25 | 3; 
console.log(result); // 27 
1
2

可见 25 和 3 的按位或操作的结果是 27:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001 
  3 = 0000 0000 0000 0000 0000 0000 0000 0011 
--------------------------------------------- 
 OR = 0000 0000 0000 0000 0000 0000 0001 1011 
1
2
3
4

(4)按位异或

按位异或用脱字符(^)表示,同样有两个操作数。下面是按位异或的真值表:

第一个数值的位 第二个数值的位 结 果
1 1 0
1 0 1
0 1 1
0 0 0

按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)。

对数值 25 和 3 执行按位异或操作:

let result = 25 ^ 3; 
console.log(result); // 26 
1
2

可见,25 和 3 的按位异或操作结果为 26,如下所示:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001 
  3 = 0000 0000 0000 0000 0000 0000 0000 0011 
--------------------------------------------- 
XOR = 0000 0000 0000 0000 0000 0000 0001 1010 
1
2
3
4

(5)左移

左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动。

比如,如果数值 2(二进制 10)向左移 5 位,就会得到 64(二进制 1000000),如下所示:

let oldValue = 2; // 等于二进制 10 
let newValue = oldValue << 5; // 等于二进制 1000000,即十进制 64 
1
2

注意在移位后,数值右端会空出 5 位。左移会以 0 填充这些空位

  10 = 0000 0000 0000 0000 0000 0000 0000 0010 
--------------------------------------------- 
左移 = 0000 0000 0000 0000 0000 0000 0100 0000 
1
2
3

注意,左移会保留它所操作数值的符号。比如,如果2 左移 5 位,将得到64,而不是正 64。

(6)有符号右移

有符号右移由两个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。

有符号右移实际上是左移的逆运算。比如,如果将 64 右移 5 位,那就是 2:

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2
1
2
  10 = 0000 0000 0000 0000 0000 0000 0100 0000 
--------------------------------------------- 
右移 = 0000 0000 0000 0000 0000 0000 0000 0010 
1
2
3

(7)无符号右移

无符号右移用 3 个大于号表示(>>>),会将数值的所有 32 位都向右移。

对于正数,无符号右移与有符号右移结果相同。仍然以前面有符号右移的例子为例,64 向右移动 5 位,会变成 2:

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >>> 5; // 等于二进制 10,即十进制 2 
1
2

对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补 0,而不管符号位是什么。对正数来说,这跟有符号右移效果相同。

 


let oldValue = -64; // 等于二进制 11111111111111111111111111000000 
let newValue = oldValue >>> 5; // 等于十进制 134217726
1
2

# 5.3 布尔操作符

布尔操作符一共有 3 个:逻辑非(!)、逻辑与(&&)与和逻辑或(||)

(1)逻辑非

逻辑非操作符由一个叹号(!)表示,可应用给 ECMAScript 中的任何值。这个操作符始终返回布尔值,无论应用到的是什么数据类型。逻辑非操作符首先将操作数转换为布尔值,然后再对其取反。换句话说,逻辑非操作符会遵循如下规则。

  • 如果操作数是对象,则返回 false。
  • 如果操作数是空字符串,则返回 true。
  • 如果操作数是非空字符串,则返回 false。
  • 如果操作数是数值 0,则返回 true。
  • 如果操作数是非 0 数值(包括 Infinity),则返回 false。
  • 如果操作数是 null,则返回 true。
  • 如果操作数是 NaN,则返回 true。
  • 如果操作数是 undefined,则返回 true。
console.log(!false); // true 
console.log(!"blue"); // false 
console.log(!0); // true 
console.log(!NaN); // true 
console.log(!""); // true 
console.log(!12345); // false 
1
2
3
4
5
6

逻辑非操作符也可以用于把任意值转换为布尔值。同时使用两个叹号(!!),相当于调用了转型函数 Boolean()。无论操作数是什么类型,第一个叹号总会返回布尔值。第二个叹号对该布尔值取反,从而给出变量真正对应的布尔值。结果与对同一个值使用 Boolean() 函数是一样的:

console.log(!!"blue"); // true 
console.log(!!0); // false 
console.log(!!NaN); // false 
console.log(!!""); // false 
console.log(!!12345); // true
1
2
3
4
5

(2)逻辑与

逻辑与操作符由两个和号(&&)表示,应用到两个值,如下所示:

let result = true && false;
1

逻辑与操作符遵循如下真值表:

第一个数值的位 第二个数值的位 结 果
true true true
true false false
false true false
false false false

逻辑与操作符可用于任何类型的操作数,不限于布尔值。如果有操作数不是布尔值,则逻辑与并不一定会返回布尔值,而是遵循如下规则。

  • 如果第一个操作数是对象,则返回第二个操作数。
  • 如果第二个操作数是对象,则只有第一个操作数求值为 true 才会返回该对象。
  • 如果两个操作数都是对象,则返回第二个操作数。
  • 如果有一个操作数是 null,则返回 null。
  • 如果有一个操作数是 NaN,则返回 NaN。
  • 如果有一个操作数是 undefined,则返回 undefined。

逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值。对逻辑与操作符来说,如果第一个操作数是 false,那么无论第二个操作数是什么值,结果也不可能等于 true。

let found = true; 
let result = (found && someUndeclaredVariable); // 这里会出错,因为 someUndeclaredVariable 没有事先声明
console.log(result); // 不会执行这一行

let found = false; 
let result = (found && someUndeclaredVariable); // 不会出错,
console.log(result); // 会执行
1
2
3
4
5
6
7

(3)逻辑或

逻辑或操作符由两个管道符(||)表示,比如:

let result = true || false; 
1

逻辑或操作符遵循如下真值表:

第一个数值的位 第二个数值的位 结 果
true true true
true false true
false true true
false false false

与逻辑与类似,如果有一个操作数不是布尔值,那么逻辑或操作符也不一定返回布尔值。它遵循如下规则。

  • 如果第一个操作数是对象,则返回第一个操作数。
  • 如果第一个操作数求值为 false,则返回第二个操作数。
  • 如果两个操作数都是对象,则返回第一个操作数。
  • 如果两个操作数都是 null,则返回 null。
  • 如果两个操作数都是 NaN,则返回 NaN。
  • 如果两个操作数都是 undefined,则返回 undefined。

同样与逻辑与类似,逻辑或操作符也具有短路的特性。只不过对逻辑或而言,第一个操作数求值为 true,第二个操作数就不会再被求值了。

let found = true; 
let result = (found || someUndeclaredVariable); // 不会出错
console.log(result); // 会执行

let found = false; 
let result = (found || someUndeclaredVariable); // 这里会出错
console.log(result); // 不会执行这一行
1
2
3
4
5
6
7

# 5.4 乘性操作符

ECMAScript 定义了 3 个乘性操作符:乘法(*)、除法(/)和取模(%)。

在处理非数值时,会进行自动的类型转换。

如果乘性操作符有不是数值的操作数,则该操作数会在后台被使用 Number() 转型函数转换为数值。这意味着空字符串会被当成 0,而布尔值 true 会被当成 1。

(1)乘法操作符

乘法操作符由一个星号(*)表示,可以用于计算两个数值的乘积。

let result = 34 * 56;
1

不过,乘法操作符在处理特殊值时也有一些特殊的行为。

  • 如果操作数都是数值,则执行常规的乘法运算,即两个正值相乘是正值,两个负值相乘也是正值,正负符号不同的值相乘得到负值。如果 ECMAScript 不能表示乘积,则返回 Infinity 或 -Infinity。
  • 如果有任一操作数是 NaN,则返回 NaN。
  • 如果是 Infinity 乘以 0,则返回 NaN。
  • 如果是 Infinity 乘以非 0的有限数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
  • 如果是 Infinity 乘以 Infinity,则返回 Infinity。
  • 如果有不是数值的操作数,则先在后台用 Number()将其转换为数值,然后再应用上述规则。

(2)除法操作符

除法操作符由一个斜杠(/)表示,用于计算第一个操作数除以第二个操作数的商,比如:

let result = 66 / 11; 
1

跟乘法操作符一样,除法操作符针对特殊值也有一些特殊的行为。

  • 如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正值,符号不同的值相除得到负值。如果ECMAScript不能表示商,则返回Infinity或-Infinity。
  • 如果有任一操作数是 NaN,则返回 NaN。
  • 如果是 Infinity 除以 Infinity,则返回 NaN。
  • 如果是 0 除以 0,则返回 NaN。
  • 如果是非 0 的有限值除以 0,则根据第一个操作数的符号返回 Infinity 或-Infinity。
  • 如果是 Infinity 除以任何数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
  • 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。

(3)取模操作符

取模(余数)操作符由一个百分比符号(%)表示,比如:

let result = 26 % 5; // 等于 1 
1

与其他乘性操作符一样,取模操作符对特殊值也有一些特殊的行为。

  • 如果操作数是数值,则执行常规除法运算,返回余数。
  • 如果被除数是无限值,除数是有限值,则返回 NaN。
  • 如果被除数是有限值,除数是 0,则返回 NaN。
  • 如果是 Infinity 除以 Infinity,则返回 NaN。
  • 如果被除数是有限值,除数是无限值,则返回被除数。
  • 如果被除数是 0,除数不是 0,则返回 0。
  • 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。

# 5.5 指数操作符

ECMAScript 7 新增了指数操作符,Math.pow() 现在有了自己的操作符 **,结果是一样的:

console.log(Math.pow(3, 2); // 9 
console.log(3 ** 2); // 9 
console.log(Math.pow(16, 0.5); // 4 
console.log(16 ** 0.5); // 4 
1
2
3
4

不仅如此,指数操作符也有自己的指数赋值操作符 **=,该操作符执行指数运算和结果的赋值操作:

let squared = 3; 
squared **= 2; 
console.log(squared); // 9 

let sqrt = 16; 
sqrt **= 0.5; 
console.log(sqrt); // 4 
1
2
3
4
5
6
7

# 5.6 加性操作符

加性操作符,即加法和减法操作符,一般都是编程语言中最简单的操作符。

(1)加法操作符

加法操作符(+)用于求两个数的和,比如:

let result = 1 + 2;
1

如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:

  • 如果有任一操作数是 NaN,则返回 NaN;
  • 如果是 Infinity 加 Infinity,则返回 Infinity;
  • 如果是-Infinity 加-Infinity,则返回-Infinity;
  • 如果是 Infinity 加-Infinity,则返回 NaN;
  • 如果是+0 加+0,则返回+0;
  • 如果是-0 加+0,则返回+0;
  • 如果是-0 加-0,则返回-0。

不过,如果有一个操作数是字符串,则要应用如下规则:

  • 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面;
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。

如果有任一操作数是对象、数值或布尔值,则调用它们的 toString() 方法以获取字符串,然后再应用前面的关于字符串的规则。对于 undefined 和 null,则调用 String() 函数,分别获取"undefined"和"null"。

(2)减法操作符

减法操作符(-)也是使用很频繁的一种操作符,比如:

let result = 2 - 1; 
1

与加法操作符一样,减法操作符也有一组规则用于处理 ECMAScript 中不同类型之间的转换。

  • 如果两个操作数都是数值,则执行数学减法运算并返回结果。
  • 如果有任一操作数是 NaN,则返回 NaN。
  • 如果是 Infinity 减 Infinity,则返回 NaN。
  • 如果是-Infinity 减-Infinity,则返回 NaN。
  • 如果是 Infinity 减-Infinity,则返回 Infinity。
  • 如果是-Infinity 减 Infinity,则返回-Infinity。
  • 如果是+0 减+0,则返回+0。
  • 如果是+0 减-0,则返回-0。
  • 如果是-0 减-0,则返回+0。
  • 如果有任一操作数是字符串、布尔值、null 或 undefined,则先在后台使用 Number()将其转换为数值,然后再根据前面的规则执行数学运算。如果转换结果是 NaN,则减法计算的结果是NaN。
  • 如果有任一操作数是对象,则调用其 valueOf()方法取得表示它的数值。如果该值是 NaN,则减法计算的结果是 NaN。如果对象没有 valueOf()方法,则调用其 toString()方法,然后再将得到的字符串转换为数值。

以下示例演示了上面的规则:

let result1 = 5 - true; // true 被转换为 1,所以结果是 4 
let result2 = NaN - 1; // NaN 
let result3 = 5 - 3; // 2 
let result4 = 5 - ""; // ""被转换为 0,所以结果是 5 
let result5 = 5 - "2"; // "2"被转换为 2,所以结果是 3 
let result6 = 5 - null; // null 被转换为 0,所以结果是 5 
1
2
3
4
5
6

# 5.7 关系操作符

关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=

用法跟数学课上学的一样。这几个操作符都返回布尔值,如下所示:

let result1 = 5 > 3; // true 
let result2 = 5 < 3; // false
1
2

ECMAScript 中的其他操作符一样,在将它们应用到不同数据类型时也会发生类型转换和其他行为。

  • 如果操作数都是数值,则执行数值比较。
  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码。
  • 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较。
  • 如果有任一操作数是对象,则调用其 valueOf() 方法,取得结果后再根据前面的规则执行比较。
  • 如果没有 valueOf() 操作符,则调用 toString() 方法,取得结果后再根据前面的规则执行比较。
  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较。

在使用关系操作符比较两个字符串时,会发生一个有趣的现象。很多人认为小于意味着“字母顺序靠前”,而大于意味着“字母顺序靠后”,实际上不是这么回事。对字符串而言,关系操作符会比较字符串中对应字符的编码,而这些编码是数值。比较完之后,会返回布尔值。问题的关键在于,大写字母的编码都小于小写字母的编码。

# 5.8 相等操作符

判断两个变量是否相等是编程中最重要的操作之一。在比较字符串、数值和布尔值是否相等时,过程都很直观。但是在比较两个对象是否相等时,情形就比较复杂了。ECMAScript 中的相等和不相等操作符,原本在比较之前会执行类型转换,但很快就有人质疑这种转换是否应该发生。

最终,ECMAScript 提供了两组操作符。第一组是等于(==)和不等于(!=),它们在比较之前执行转换。第二组是全等(===)和不全等(!==),它们在比较之前不执行转换。

(1)等于和不等于

ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。不等于操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。这两个操作符都会先进行类型转换(通常称为强制类型转换)再确定操作数是否相等。

在转换操作数的类型时,相等和不相等操作符遵循如下规则。

  • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为 0,true 转换为 1。
  • 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法取得其原始值,再

根据前面的规则进行比较。

在进行比较时,这两个操作符会遵循如下规则。

  • null 和 undefined 相等。
  • null 和 undefined 不能转换为其他类型的值再进行比较。
  • 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true。记住:即使两个操作数都是 NaN,相等操作符也返回 false,因为按照规则,NaN 不等于 NaN。
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true。否则,两者不相等。

下表总结了一些特殊情况及比较的结果

表 达 式 结 果
null == undefined true
"NaN" == NaN false
5 == NaN false
NaN == NaN false
NaN != NaN true
false == 0 true
true == 1 true
true == 2 false
undefined == 0 false
null == 0 false
"5" == 5 true

(2)全等和不全等

全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。

全等操作符由 3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回 true,比如:

let result1 = ("55" == 55); // true,转换后相等
let result2 = ("55" === 55); // false,不相等,因为数据类型不同
1
2

不全等操作符用一个叹号和两个等于号(!==)表示,只有两个操作数在不转换的前提下不相等才返回 true。比如:

let result1 = ("55" != 55); // false,转换后相等
let result2 = ("55" !== 55); // true,不相等,因为数据类型不同
1
2

由于相等和不相等操作符存在类型转换问题,因此推荐使用全等和不全等操作符。这样有助于在代码中保持数据类型的完整性。

# 5.9 条件操作符

条件操作符是 ECMAScript 中用途最为广泛的操作符之一,语法跟 Java 中一样:

variable = boolean_expression ? true_value : false_value;

let max = (num1 > num2) ? num1 : num2;
1
2
3

# 5.10 赋值操作符

简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量,如下所示:

let num = 10; 
1

复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。这些赋值操作符是类似如下常见赋值操作的简写形式:

let num = 10; 
num = num + 10; 
1
2

以上代码的第二行可以通过复合赋值来完成:

let num = 10; 
num += 10; 
1
2

每个数学操作符以及其他一些操作符都有对应的复合赋值操作符:

  • 乘后赋值(*=)
  • 除后赋值(/=)
  • 取模后赋值(%=)
  • 加后赋值(+=)
  • 减后赋值(-=)
  • 左移后赋值(<<=)
  • 右移后赋值(>>=)
  • 无符号右移后赋值(>>>=)

这些操作符仅仅是简写语法,使用它们不会提升性能。

# 5.11 逗号操作符

逗号操作符可以用来在一条语句中执行多个操作,如下所示:

let num1 = 1, num2 = 2, num3 = 3; 
1

在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值:

let num = (5, 1, 4, 8, 0); // num 的值为 0 
1

在这个例子中,num 将被赋值为 0,因为 0 是表达式中最后一项。逗号操作符的这种使用场景并不多见,但这种行为的确存在。

# 6、语句

ECMA-262 描述了一些语句(也称为流控制语句),而 ECMAScript 中的大部分语法都体现在语句中。语句通常使用一或多个关键字完成既定的任务。语句可以简单,也可以复杂。

# 6.1 if 语句

if 语句是使用最频繁的语句之一,语法如下:

if (condition) statement1 else statement2 
1

这里的条件(condition)可以是任何表达式,并且求值结果不一定是布尔值。ECMAScript 会自动调用 Boolean() 函数将这个表达式的值转换为布尔值。如果条件求值为 true,则执行语句 statement1;如果条件求值为 false,则执行语句 statement2。这里的语句可能是一行代码,也可能是一个代码块(即包含在一对花括号中的多行代码)。来看下面的例子:

if (i > 25) 
 console.log("Greater than 25."); // 只有一行代码的语句
else { 
 console.log("Less than or equal to 25."); // 一个语句块
} 
1
2
3
4
5

这里的最佳实践是使用语句块,即使只有一行代码要执行也是如此。这是因为语句块可以避免对什么条件下执行什么产生困惑 可以像这样连续使用多个 if 语句:

if (condition1) statement1 else if (condition2) statement2 else statement3 
1

下面是一个例子:

if (i > 25) { 
 console.log("Greater than 25."); 
} else if (i < 0) { 
 console.log("Less than 0."); 
} else { 
 console.log("Between 0 and 25, inclusive."); 
}
1
2
3
4
5
6
7

# 6.2 do-while 语句

do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行一次。do-while 的语法如下:

do { 
 statement 
} while (expression); 
1
2
3

下面是一个例子:

let i = 0; 
do { 
 i += 2; 
} while (i < 10); 
1
2
3
4

在这个例子中,只要 i 小于 10,循环就会重复执行。i 从 0 开始,每次循环递增 2。

注意 后测试循环经常用于这种情形:循环体内代码在退出前至少要执行一次。

# 6.3 while 语句

while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。因此,while 循环体内的代码有可能不会执行。

下面是 while 循环的语法:

while(expression) statement 
1

这是一个例子:

let i = 0; 
while (i < 10) { 
 i += 2; 
} 
1
2
3
4

在这个例子中,变量 i 从 0 开始,每次循环递增 2。只要 i 小于 10,循环就会继续。

# 6.4 for 语句

for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表达式,语法如下:

for (initialization; expression; post-loop-expression) statement
1

初始化、条件表达式和循环后表达式都不是必需的。因此,下面这种写法可以创建一个无穷循环:

for (;;) { // 无穷循环
 doSomething(); 
} 
1
2
3

如果只包含条件表达式,那么 for 循环实际上就变成了 while 循环:

let count = 10; 
let i = 0; 
for (; i < count; ) { 
 console.log(i); 
 i++; 
} 
1
2
3
4
5
6

这种多功能性使得 for 语句在这门语言中使用非常广泛。

# 6.5 for-in 语句

for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,语法如下:

for (property in expression) statement 
1

下面是一个例子:

for (const propName in window) { 
 document.write(propName); 
} 
1
2
3

这个例子使用 for-in 循环显示了 BOM 对象 window 的所有属性。每次执行循环,都会给变量 propName 赋予一个 window 对象的属性作为值,直到 window 的所有属性都被枚举一遍。与 for 循环一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const。

ECMAScript 中对象的属性是无序的,因此 for-in 语句不能保证返回对象属性的顺序。换句话说,所有可枚举的属性都会返回一次,但返回的顺序可能会因浏览器而异。

如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体。

# 6.6 for-of 语句

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素,语法如下:

for (property of expression) statement 
1

下面是示例:

for (const el of [2,4,6,8]) { 
 document.write(el); 
}
1
2
3

在这个例子中,我们使用 for-of 语句显示了一个包含 4 个元素的数组中的所有元素。循环会一直持续到将所有元素都迭代完。与 for 循环一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const。

for-of 循环会按照可迭代对象的 next() 方法产生值的顺序迭代元素。如果尝试迭代的变量不支持迭代,则 for-of 语句会抛出错误。

注意 ES2018 对 for-of 语句进行了扩展,增加了 for-await-of 循环,以支持生成期约(promise)的异步可迭代对象。相关内容将在附录 A 介绍。

# 6.7 标签语句

标签语句用于给语句加标签,语法如下:

label: statement
1

下面是一个例子:

start: for (let i = 0; i < count; i++) { 
 console.log(i); 
} 
1
2
3

在这个例子中,start 是一个标签,可以在后面通过 break 或 continue 语句引用。标签语句的典型应用场景是嵌套循环。

# 6.8 break 和 continue 语句

break 和 continue 语句为执行循环代码提供了更严格的控制手段。其中,break 语句用于立即退出循环,强制执行循环后的下一条语句。而 continue 语句也用于立即退出循环,但会再次从循环顶部

开始执行。下面看一个例子:

let num = 0; 
for (let i = 1; i < 10; i++) { 
 if (i % 5 == 0) { 
 break;
  } 
 num++; 
} 
console.log(num); // 4 
1
2
3
4
5
6
7
8

如果将 break 换成 continue,则会出现不同的效果:

let num = 0; 
for (let i = 1; i < 10; i++) { 
 if (i % 5 == 0) { 
 continue; 
 } 
 num++; 
} 
console.log(num); // 8 
1
2
3
4
5
6
7
8

# 6.9 with 语句

with 语句的用途是将代码作用域设置为特定的对象,其语法是:

with (expression) statement; 
1

使用 with 语句的主要场景是针对一个对象反复操作,这时候将代码作用域设置为该对象能提供便 利,如下面的例子所示:

let qs = location.search.substring(1); 
let hostName = location.hostname; 
let url = location.href; 
1
2
3

上面代码中的每一行都用到了 location 对象。如果使用 with 语句,就可以少写一些代码:

with(location) { 
 let qs = search.substring(1); 
 let hostName = hostname; 
 let url = href; 
}
1
2
3
4
5

严格模式不允许使用 with 语句,否则会抛出错误。

警告 由于 with 语句影响性能且难于调试其中的代码,通常不推荐在产品代码中使用 with 语句。

# 6.10 switch 语句

switch 语句是与 if 语句紧密相关的一种流控制语句,从其他语言借鉴而来。ECMAScript中 switch 语句跟 C 语言中 switch 语句的语法非常相似,如下所示:

有了 switch 语句,开发者就用不着写类似这样的代码了:

if (i == 25) { 
 console.log("25"); 
} else if (i == 35) { 
 console.log("35"); 
} else if (i == 45) { 
 console.log("45"); 
} else { 
 console.log("Other"); 
} 
1
2
3
4
5
6
7
8
9

而是可以这样写:

switch (i) { 
 case 25: 
 console.log("25"); 
 break; 
 case 35: 
 console.log("35"); 
 break; 
 case 45: 
 console.log("45"); 
 break; 
 default: 
 console.log("Other"); 
}
1
2
3
4
5
6
7
8
9
10
11
12
13

为避免不必要的条件判断,最好给每个条件后面都加上 break 语句。如果确实需要连续匹配几个条件,那么推荐写个注释表明是故意忽略了 break,如下所示:

# 7、函数

函数对任何语言来说都是核心组件,因为它们可以封装语句,然后在任何地方、任何时间执行。ECMAScript 中的函数使用 function 关键字声明,后跟一组参数,然后是函数体。

详情见js函数

# 8、总结

  1. ECMAScript 包含所有基本语法、操作符、数据类型和对象,能完成基本的计算任务,但没有提供获得输入和产生输出的机制。

  2. 语法

  • 区分大小写
  • 注释:支持单行和多行注释
  • 严格模式:ES5 新增,"use strict";
  • 语句:以分号结尾
  1. 关键字
  • break do in typeof
  • case else instanceof var
  • catch export new void
  • class extends return while
  • const finally super with
  • continue for switch yield
  • debugger function this
  • default if throw
  • delete import try
  1. 变量,3 个关键字可以声明变量:varconstlet

  2. 数据类型

  • 7 种简单数据类型(也称为原始类型):Undefined、Null、Boolean、Number、BigInt、String 和 Symbol。
  • 还有一种复杂数据类型叫 Object(对象)。Object 是一种无序名值对的集合。
  1. 操作符
  • 一元运算符
    • ++ 递增
    • -- 递减
  • 位操作符
    • ~ 按位非 操作符用波浪符
    • & 按位与 操作符用和号
    • | 按位或 操作符用管道符
    • ^ 按位异或 用脱字符
    • << 左移 操作符用两个小于号
    • >> 有符号右移 由两个大于号
    • >>> 无符号右移 用 3 个大于号表示
  • 布尔操作符
    • ! 逻辑非 操作符由一个叹号
    • && 逻辑与 操作符由两个和号
    • || 逻辑或 操作符由两个管道符
  • 乘性操作符
    • * 乘法 操作符由一个星号
    • / 除法 操作符由一个斜杠
    • % 取模(余数) 操作符由一个百分比符号
  • 指数操作符
    • ** 等价于 Math.pow()
    • **= 指数赋值操作符
  • 加性操作符
    • + 加法 操作符用于求两个数的和
    • - 减法 操作符用于求两个数的差
  • 关系操作符,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=
  • 相等操作符
    • ==,!= 等于和不等于
    • ===,!== 全等和不全等
  • 条件操作符,let max = (num1 > num2) ? num1 : num2;
  • 赋值操作符,简单赋值用等于号(=)表示
  • 逗号操作符,可以用来在一条语句中执行多个操作
  1. 语句
  • if 语句
  • do-while 语句
  • while 语句
  • for 语句
  • for-in 语句
  • for-of 语句
  • 标签 语句
  • break/continue 语句
  • with 语句
  • switch 语句
  1. 函数
  • 不需要指定函数的返回值,因为任何函数可以在任何时候返回任何值。
  • 不指定返回值的函数实际上会返回特殊值 undefined。
上次更新: 4/10/2022, 10:26:50 AM