# 语言基础
# 1、语法
# 1.1 区分大小写
无论是变量、函数名还是操作符,都区分大小写。
# 1.2 标识符
所谓标识符,就是变量、函数、属性或函数参数的名称。标识符可以由一或多个下列字符组成:
- 第一个字符必须是一个字母、下划线(_)或美元符号($);
- 剩下的其他字符可以是字母、下划线、美元符号或数字。
标识符中的字母可以是扩展 ASCII(Extended ASCII)中的字母,也可以是 Unicode 的字母字符,如 À 和 Æ(但不推荐使用)
# 1.3 注释
分为单行注释和多行注释:
// 单行注释
/* 多行
注释 */
2
3
4
# 1.4 严格模式
ECMAScript 5 增加了严格模式(strict mode)的概念。
主要用于对不安全的活动将抛出错误。要对整个脚本启用严格模式,在脚本开头加上这一行:
"use strict";
也可以作用在函数块:
function test() {
"user strict";
// 函数体
}
2
3
4
# 1.5 语句
ECMAScript 中的语句以分号结尾。省略分号意味着由解析器确定语句在哪里结尾。
let sum = a + b // 没有分号也有效,但不推荐
let diff = a - b; // 加分号有效,推荐
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 个关键字可以声明变量:var
、const
和 let
。其中,var 在 ECMAScript 的所有版本中都可以使用,而 const 和 let 只能在 ECMAScript 6 及更晚的版本中使用。
详解见js变量详解
# 4、数据类型
ECMAScript 有 7 种简单数据类型(也称为原始类型):Undefined
、Null
、Boolean
、Number
、BigInt
、String
和 Symbol
。还有一种复杂数据类型叫 Object
(对象)。Object
是一种无序名值对的集合。
详解见js数据类型
# 5、操作符
ECMA-262 描述了一组可用于操作数据值的操作符,包括数学操作符(如加、减)、位操作符、关系操作符和相等操作符等。
在应用给对象时,操作符通常会调用 valueOf() 和/或 toString() 方法来取得可以计算的值。
# 5.1 一元运算符
(1)递增/递减操作符
// 递增
let age = 29;
++age;
// 递减
let age = 29;
--age;
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
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
2
3
就像执行如下操作的结果一样:
let num1 = 25;
let num2 = -num1 - 1;
console.log(num2); // "-26"
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
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
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
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
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
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
2
3
4
(5)左移
左移操作符用两个小于号(<<
)表示,会按照指定的位数将数值的所有位向左移动。
比如,如果数值 2(二进制 10)向左移 5 位,就会得到 64(二进制 1000000),如下所示:
let oldValue = 2; // 等于二进制 10
let newValue = oldValue << 5; // 等于二进制 1000000,即十进制 64
2
注意在移位后,数值右端会空出 5 位。左移会以 0 填充这些空位
10 = 0000 0000 0000 0000 0000 0000 0000 0010
---------------------------------------------
左移 = 0000 0000 0000 0000 0000 0000 0100 0000
2
3
注意,左移会保留它所操作数值的符号。比如,如果2 左移 5 位,将得到64,而不是正 64。
(6)有符号右移
有符号右移由两个大于号(>>
)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。
有符号右移实际上是左移的逆运算。比如,如果将 64 右移 5 位,那就是 2:
let oldValue = 64; // 等于二进制 1000000
let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2
2
10 = 0000 0000 0000 0000 0000 0000 0100 0000
---------------------------------------------
右移 = 0000 0000 0000 0000 0000 0000 0000 0010
2
3
(7)无符号右移
无符号右移用 3 个大于号表示(>>>),会将数值的所有 32 位都向右移。
对于正数,无符号右移与有符号右移结果相同。仍然以前面有符号右移的例子为例,64 向右移动 5 位,会变成 2:
let oldValue = 64; // 等于二进制 1000000
let newValue = oldValue >>> 5; // 等于二进制 10,即十进制 2
2
对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补 0,而不管符号位是什么。对正数来说,这跟有符号右移效果相同。
let oldValue = -64; // 等于二进制 11111111111111111111111111000000
let newValue = oldValue >>> 5; // 等于十进制 134217726
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
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
2
3
4
5
(2)逻辑与
逻辑与操作符由两个和号(&&
)表示,应用到两个值,如下所示:
let result = true && false;
逻辑与操作符遵循如下真值表:
第一个数值的位 | 第二个数值的位 | 结 果 |
---|---|---|
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); // 会执行
2
3
4
5
6
7
(3)逻辑或
逻辑或操作符由两个管道符(||
)表示,比如:
let result = true || false;
逻辑或操作符遵循如下真值表:
第一个数值的位 | 第二个数值的位 | 结 果 |
---|---|---|
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); // 不会执行这一行
2
3
4
5
6
7
# 5.4 乘性操作符
ECMAScript 定义了 3 个乘性操作符:乘法(*
)、除法(/
)和取模(%
)。
在处理非数值时,会进行自动的类型转换。
如果乘性操作符有不是数值的操作数,则该操作数会在后台被使用 Number() 转型函数转换为数值。这意味着空字符串会被当成 0,而布尔值 true 会被当成 1。
(1)乘法操作符
乘法操作符由一个星号(*
)表示,可以用于计算两个数值的乘积。
let result = 34 * 56;
不过,乘法操作符在处理特殊值时也有一些特殊的行为。
- 如果操作数都是数值,则执行常规的乘法运算,即两个正值相乘是正值,两个负值相乘也是正值,正负符号不同的值相乘得到负值。如果 ECMAScript 不能表示乘积,则返回 Infinity 或 -Infinity。
- 如果有任一操作数是 NaN,则返回 NaN。
- 如果是 Infinity 乘以 0,则返回 NaN。
- 如果是 Infinity 乘以非 0的有限数值,则根据第二个操作数的符号返回 Infinity 或-Infinity。
- 如果是 Infinity 乘以 Infinity,则返回 Infinity。
- 如果有不是数值的操作数,则先在后台用 Number()将其转换为数值,然后再应用上述规则。
(2)除法操作符
除法操作符由一个斜杠(/
)表示,用于计算第一个操作数除以第二个操作数的商,比如:
let result = 66 / 11;
跟乘法操作符一样,除法操作符针对特殊值也有一些特殊的行为。
- 如果操作数都是数值,则执行常规的除法运算,即两个正值相除是正值,两个负值相除也是正值,符号不同的值相除得到负值。如果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
与其他乘性操作符一样,取模操作符对特殊值也有一些特殊的行为。
- 如果操作数是数值,则执行常规除法运算,返回余数。
- 如果被除数是无限值,除数是有限值,则返回 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
2
3
4
不仅如此,指数操作符也有自己的指数赋值操作符 **=
,该操作符执行指数运算和结果的赋值操作:
let squared = 3;
squared **= 2;
console.log(squared); // 9
let sqrt = 16;
sqrt **= 0.5;
console.log(sqrt); // 4
2
3
4
5
6
7
# 5.6 加性操作符
加性操作符,即加法和减法操作符,一般都是编程语言中最简单的操作符。
(1)加法操作符
加法操作符(+
)用于求两个数的和,比如:
let result = 1 + 2;
如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:
- 如果有任一操作数是 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;
与加法操作符一样,减法操作符也有一组规则用于处理 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
2
3
4
5
6
# 5.7 关系操作符
关系操作符执行比较两个值的操作,包括小于(<
)、大于(>
)、小于等于(<=
)和大于等于(>=
)
用法跟数学课上学的一样。这几个操作符都返回布尔值,如下所示:
let result1 = 5 > 3; // true
let result2 = 5 < 3; // false
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,不相等,因为数据类型不同
2
不全等操作符用一个叹号和两个等于号(!==
)表示,只有两个操作数在不转换的前提下不相等才返回 true。比如:
let result1 = ("55" != 55); // false,转换后相等
let result2 = ("55" !== 55); // true,不相等,因为数据类型不同
2
由于相等和不相等操作符存在类型转换问题,因此推荐使用全等和不全等操作符。这样有助于在代码中保持数据类型的完整性。
# 5.9 条件操作符
条件操作符是 ECMAScript 中用途最为广泛的操作符之一,语法跟 Java 中一样:
variable = boolean_expression ? true_value : false_value;
let max = (num1 > num2) ? num1 : num2;
2
3
# 5.10 赋值操作符
简单赋值用等于号(=
)表示,将右手边的值赋给左手边的变量,如下所示:
let num = 10;
复合赋值使用乘性、加性或位操作符后跟等于号(=
)表示。这些赋值操作符是类似如下常见赋值操作的简写形式:
let num = 10;
num = num + 10;
2
以上代码的第二行可以通过复合赋值来完成:
let num = 10;
num += 10;
2
每个数学操作符以及其他一些操作符都有对应的复合赋值操作符:
- 乘后赋值(*=)
- 除后赋值(/=)
- 取模后赋值(%=)
- 加后赋值(+=)
- 减后赋值(-=)
- 左移后赋值(<<=)
- 右移后赋值(>>=)
- 无符号右移后赋值(>>>=)
这些操作符仅仅是简写语法,使用它们不会提升性能。
# 5.11 逗号操作符
逗号操作符可以用来在一条语句中执行多个操作,如下所示:
let num1 = 1, num2 = 2, num3 = 3;
在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值:
let num = (5, 1, 4, 8, 0); // num 的值为 0
在这个例子中,num 将被赋值为 0,因为 0 是表达式中最后一项。逗号操作符的这种使用场景并不多见,但这种行为的确存在。
# 6、语句
ECMA-262 描述了一些语句(也称为流控制语句),而 ECMAScript 中的大部分语法都体现在语句中。语句通常使用一或多个关键字完成既定的任务。语句可以简单,也可以复杂。
# 6.1 if 语句
if 语句是使用最频繁的语句之一,语法如下:
if (condition) statement1 else statement2
这里的条件(condition)可以是任何表达式,并且求值结果不一定是布尔值。ECMAScript 会自动调用 Boolean() 函数将这个表达式的值转换为布尔值。如果条件求值为 true,则执行语句 statement1;如果条件求值为 false,则执行语句 statement2。这里的语句可能是一行代码,也可能是一个代码块(即包含在一对花括号中的多行代码)。来看下面的例子:
if (i > 25)
console.log("Greater than 25."); // 只有一行代码的语句
else {
console.log("Less than or equal to 25."); // 一个语句块
}
2
3
4
5
这里的最佳实践是使用语句块,即使只有一行代码要执行也是如此。这是因为语句块可以避免对什么条件下执行什么产生困惑 可以像这样连续使用多个 if 语句:
if (condition1) statement1 else if (condition2) statement2 else statement3
下面是一个例子:
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.");
}
2
3
4
5
6
7
# 6.2 do-while 语句
do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行一次。do-while 的语法如下:
do {
statement
} while (expression);
2
3
下面是一个例子:
let i = 0;
do {
i += 2;
} while (i < 10);
2
3
4
在这个例子中,只要 i 小于 10,循环就会重复执行。i 从 0 开始,每次循环递增 2。
注意 后测试循环经常用于这种情形:循环体内代码在退出前至少要执行一次。
# 6.3 while 语句
while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。因此,while 循环体内的代码有可能不会执行。
下面是 while 循环的语法:
while(expression) statement
这是一个例子:
let i = 0;
while (i < 10) {
i += 2;
}
2
3
4
在这个例子中,变量 i 从 0 开始,每次循环递增 2。只要 i 小于 10,循环就会继续。
# 6.4 for 语句
for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表达式,语法如下:
for (initialization; expression; post-loop-expression) statement
初始化、条件表达式和循环后表达式都不是必需的。因此,下面这种写法可以创建一个无穷循环:
for (;;) { // 无穷循环
doSomething();
}
2
3
如果只包含条件表达式,那么 for 循环实际上就变成了 while 循环:
let count = 10;
let i = 0;
for (; i < count; ) {
console.log(i);
i++;
}
2
3
4
5
6
这种多功能性使得 for 语句在这门语言中使用非常广泛。
# 6.5 for-in 语句
for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,语法如下:
for (property in expression) statement
下面是一个例子:
for (const propName in window) {
document.write(propName);
}
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
下面是示例:
for (const el of [2,4,6,8]) {
document.write(el);
}
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
下面是一个例子:
start: for (let i = 0; i < count; i++) {
console.log(i);
}
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
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
2
3
4
5
6
7
8
# 6.9 with 语句
with 语句的用途是将代码作用域设置为特定的对象,其语法是:
with (expression) statement;
使用 with 语句的主要场景是针对一个对象反复操作,这时候将代码作用域设置为该对象能提供便 利,如下面的例子所示:
let qs = location.search.substring(1);
let hostName = location.hostname;
let url = location.href;
2
3
上面代码中的每一行都用到了 location 对象。如果使用 with 语句,就可以少写一些代码:
with(location) {
let qs = search.substring(1);
let hostName = hostname;
let url = href;
}
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");
}
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");
}
2
3
4
5
6
7
8
9
10
11
12
13
为避免不必要的条件判断,最好给每个条件后面都加上 break 语句。如果确实需要连续匹配几个条件,那么推荐写个注释表明是故意忽略了 break,如下所示:
# 7、函数
函数对任何语言来说都是核心组件,因为它们可以封装语句,然后在任何地方、任何时间执行。ECMAScript 中的函数使用 function 关键字声明,后跟一组参数,然后是函数体。
详情见js函数
# 8、总结
ECMAScript 包含所有基本语法、操作符、数据类型和对象,能完成基本的计算任务,但没有提供获得输入和产生输出的机制。
语法
- 区分大小写
- 注释:支持单行和多行注释
- 严格模式:ES5 新增,
"use strict";
- 语句:以分号结尾
- 关键字
- 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
变量,3 个关键字可以声明变量:
var
、const
和let
。数据类型
- 7 种简单数据类型(也称为原始类型):Undefined、Null、Boolean、Number、BigInt、String 和 Symbol。
- 还有一种复杂数据类型叫 Object(对象)。Object 是一种无序名值对的集合。
- 操作符
- 一元运算符
++
递增--
递减
- 位操作符
~
按位非 操作符用波浪符&
按位与 操作符用和号|
按位或 操作符用管道符^
按位异或 用脱字符<<
左移 操作符用两个小于号>>
有符号右移 由两个大于号>>>
无符号右移 用 3 个大于号表示
- 布尔操作符
!
逻辑非 操作符由一个叹号&&
逻辑与 操作符由两个和号||
逻辑或 操作符由两个管道符
- 乘性操作符
*
乘法 操作符由一个星号/
除法 操作符由一个斜杠%
取模(余数) 操作符由一个百分比符号
- 指数操作符
**
等价于Math.pow()
**=
指数赋值操作符
- 加性操作符
+
加法 操作符用于求两个数的和-
减法 操作符用于求两个数的差
- 关系操作符,包括小于(
<
)、大于(>
)、小于等于(<=
)和大于等于(>=
) - 相等操作符
==,!=
等于和不等于===,!==
全等和不全等
- 条件操作符,
let max = (num1 > num2) ? num1 : num2;
- 赋值操作符,简单赋值用等于号(
=
)表示 - 逗号操作符,可以用来在一条语句中执行多个操作
- 语句
if
语句do-while
语句while
语句for
语句for-in
语句for-of
语句标签
语句break/continue
语句with
语句switch
语句
- 函数
- 不需要指定函数的返回值,因为任何函数可以在任何时候返回任何值。
- 不指定返回值的函数实际上会返回特殊值 undefined。