概览

  • 普通语句:
  • 声明型语句:

声明型语句跟普通语句最大区别就是声明型语句响应预处理过程,普通语句只有执行过程。

普通语句

语句块

我们可以这样去简单理解,语句块就是一对大括号。

1
2
3
4
5
{
var x, y;
x = 10;
y = 20;
}

空语句

空语句就是一个独立的分号,实际上没什么大用。我们来看一下:

1
;

if 语句

if 语句是条件语句。我想,对多数人来说,if 语句都是熟悉的老朋友了,也没有什么特别需要注意的用法

switch 语句

switch 语句继承自 Java,Java 中的 switch 语句继承自 C 和 C++,原本 switch 语句是跳转的变形,所以我们如果要用它来实现分支,必须要加上 break。

1
2
3
4
5
6
7
8
switch(num) {
case 1:
print(1);
case 2:
print 2;
case 3:
print 3;
}

这段代码当 num 为 1 时输出 1 2 3,当 num 为 2 时输出 2 3,当 num 为 3 时输出 3。如果我们要把它变成分支型,则需要在每个 case 后加上 break。

1
2
3
4
5
6
7
8
9
10
11
switch(num) {
case 1:
print 1;
break;
case 2:
print 2;
break;
case 3:
print 3;
break;
}

在 C 时代,switch 生成的汇编代码性能是略优于 if else 的,但是对 JavaScript 来说,则无本质区别。我个人的看法是,现在 switch 已经完全没有必要使用了,应该用 if else 结构代替。

循环语句

循环语句应该也是你所熟悉的语句了

while 循环和 do while 循环

1
2
3
4
let a = 100
while(a--) {
console.log("*");
}
1
2
3
4
let a = 101;
do {
console.log(a);
} while(a < 100)

注意,这里 do while 循环无论如何至少会执行一次。

普通 for 循环

普通的 for 循环

for in 循环

for in 循环枚举对象的属性,这里体现了属性的 enumerable 特征。

for of 循环和 for await of 循环

for of 循环是非常棒的语法特性。

我们先看下基本用法,它可以用于数组:

1
2
for(let e of [1, 2, 3, 4, 5])
console.log(e);

但是实际上,它背后的机制是 iterator 机制。

我们可以给任何一个对象添加 iterator,使它可以用于 for of 语句,看下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let o = {  
[Symbol.iterator]:() => ({
_value: 0,
next(){
if(this._value == 10)
return {
done: true
}
else return {
value: this._value++,
done: false
};
}
})
}
for(let e of o)
console.log(e);

这段代码展示了如何为一个对象添加 iterator。但是,在实际操作中,我们一般不需要这样定义 iterator,我们可以使用 generator function。

1
2
3
4
5
6
7
8
function* foo(){
yield 0;
yield 1;
yield 2;
yield 3;
}
for(let e of foo())
console.log(e);

这段代码展示了 generator function 和 foo 的配合。

此外,JavaScript 还为异步生成器函数配备了异步的 for of,我们来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function sleep(duration) {
return new Promise(function(resolve, reject) {
setTimeout(resolve,duration);
})
}
async function* foo(){
i = 0;
while(true) {
await sleep(1000);
yield i++;
}

}
for await(let e of foo())
console.log(e);

这段代码定义了一个异步生成器函数,异步生成器函数每隔一秒生成一个数字,这是一个无限的生成器。

接下来,我们使用 for await of 来访问这个异步生成器函数的结果,我们可以看到,这形成了一个每隔一秒打印一个数字的无限循环。

return

return 语句用于函数中,它终止函数的执行,并且指定函数的返回值,这是大家非常熟悉语句了,也没有什么特殊之处。

break 语句和 continue 语句

break 语句用于跳出循环语句或者 switch 语句,continue 语句用于结束本次循环并继续循环。

with 语句

with 语句是个非常巧妙的设计,但它把 JavaScript 的变量引用关系变得不可分析,所以一般都认为这种语句都属于糟粕。

1
2
3
4
let o = {a:1, b:2}
with(o){
console.log(a, b);
}

with 语句把对象的属性在它内部的作用域内变成变量。

try 语句和 throw 语句

try 语句和 throw 语句用于处理异常。它们是配合使用的,所以我们就放在一起讲了。在大型应用中,异常机制非常重要。

debugger 语句

debugger 语句的作用是:通知调试器在此断点。在没有调试器挂载时,它不产生任何效果。

声明型语句

var

var 声明语句是古典的 JavaScript 中声明变量的方式。而现在,在绝大多数情况下,let 和 const

如果我们仍然想要使用 var,我的个人建议是,把它当做一种“保障变量是局部”的逻辑,遵循以下三条规则:

  1. 声明同时必定初始化;
  2. 尽可能在离使用的位置近处声明;
  3. 不要在意重复声明。

let 和 const

let 和 const 是都是变量的声明,它们的特性非常相似,所以我们放在一起讲了。let 和 const 是新设计的语法,所以没有什么硬伤,非常地符合直觉。let 和 const 的作用范围是 if、for 等结构型语句。

class 声明

我们在之前的课程中,已经了解过 class 相关的用法。这里我们再从语法的角度来看一遍:

class 最基本的用法只需要 class 关键字、名称和一对大括号。它的声明特征跟 const 和 let 类似,都是作用于块级作用域,预处理阶段则会屏蔽外部变量。

1
2
3
4
5
6
7
const a = 2;
if(true){
console.log(a); //抛错
class a {

}
}

class 内部,可以使用 constructor 关键字来定义构造函数。还能定义 getter/setter 和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
}

以目前的兼容性,class 中的属性只能写在构造函数中,相关标准正在 TC39 讨论。

需要注意,class 默认内部的函数定义都是 strict 模式的。

函数声明

函数声明使用 function 关键字。