一、语句和表达式
- 开发人员常常将“语句”和“表达式”混淆,这里先区分一下。
- 语句相当于句子,完整地表达某个意思的一组词。
- 表达式相当于短语,运算符则相当于标点符号和连接词。
var a = 3 * 6;
var b = a;
b;
3 * 6
是一个表达式,第二行的a和第三行的b也是表达式。a = 3 * 6
和b =a
是赋值表达式- 第一行和第二行是声明语句,因为它们声明了变量。第三行是表达式b,同时也是语句,叫做表达式语句。
1. 语句的结果值
- 语句都有一个结果值
var a = 3 * 6;
var b = a; // 结果是赋值给b的值,18
- 在浏览器开发控制台中输入语句,默认情况下控制台会显示所执行的最后一条语句的结果值。
if (true) {
b = 4 + 38;
}
- 代码块也是返回最后一个语句的结果值。
2. 表达式的副作用
最常见的是函数调用
-
a++
,先返回变量a的值,再对a的值加1。(还有++a
,a--
,--a
) -
a = b += 2
,即b = b + 2
,然后a = b
3. 上下文规则
- 大括号
对象常量
var obj = {
a: 1;
}
标签语句
{
a: 1;
}
- 代码块
[] + {}; // "[object Object]",这里的{}为对象
{} + []; // 0,这里的{}为代码块
- 对象解构
ES6
开始,{}
也用于解构赋值。
- else if和可选代码块
- 实际上JavaScript没有
else if
,但if
或else
只包含单条语句的时候可以省略代码块的{}
。
if(a) doSomething(b) // 省略了{}
二、运算符的优先级
- 表达式中出现两个或两个以上运算符就需要注意运算符的顺序。
参见MDN网站的“优先级表“。
运算符优先级 - JavaScript | MDN (mozilla.org)
这里介绍常见的:
1.短路
- 对
&&
和||
来说,如果左边的操作能够得出结果,就可以忽略右边的操作数。
a && a.name;
-
a条件的判断如同一道安全保护,如果a未赋值,表达式
a.name
会出错。 -
通过短路特性,a条件判断未通过时,
a.name
不会执行。
2.关联
-
如果多个相同优先级的运算符同时出现
-
运算符的关联不是从左到右就是从右到左,这取决于组合式从左开始还是从右开始。
3.使用
- 适当结合
()
使用,增强可读性。
三、自动分号
- 有时JavaScript会自动为代码补上分号,即自动分号插入(
ASI
)。 - 如果缺失了必要的分号,代码将无法运行。
var a = 42, b
c;
- 如果没有分号,c会被作为
var语句
的一部分来处理。JavaScript会判断后会自动补充。
是否应该完全依赖ASI来编码,这是JavaScript中最具争议的话题之一。
-
正方认为
ASI
机制能省掉哪些不必要的分号; -
反方认为
ASI
机制问题太多,尤其是对于缺乏经验的初学者,因为自动插入分号,会无意中改变代码的逻辑。 -
作者建议在所有需要的地方加上分号,将
ASI
的依赖降到最低。
四、错误
1. 语法错误
- 在编译阶段发现的代码错误叫做”早期错误“。语法错误是早期错误的一种。
- 这种错误无法通过
try...catch
来捕获,相反,还会导致解析失败。
var a;
42 = a; // 错误
2. 提前使用变量
ES6
规范定义了一个新的概念,叫做暂性死区。(属于语法错误)- 暂性死区是由于代码中的变量还没有初始化而不能被引用的情况。
{
typeof a; // undefined,这里不会报错
typeof b; // ReferenceError!
let b
}
五、函数参数
ES6
参数默认值或函数不传入参数时,会导致arguments
数组和相对应的命名参数之间出现偏差。
function foo(a) {
a = 42;
console.log(arguments[0])
}
foo(2); // 42
foo(); // undefined
-
也就是说,
arguments
建立传入参数的关联,而且在严格模式下,不会建立关联。 -
因此,在开发中不要依赖
arguments
关联机制。
六、try…finally
- 如果有
catch
的话是在catch
中执行。 - 但是无论出现什么情况,最后一定会调用
finally
。
七、switch
- 可以把它看作
if...else if...else if...
的简化版本
var a = "42";
switch (true) { // 匹配true
case a == 10:
// ...
break;
case a == 42:
// ...
break;
default:
// 没有匹配则执行这里
}
// 或者
switch (a) { // 匹配a
case 42:
// ...
break;
}
- 需要注意的是,case的匹配算法和 ===相同,通常是简单值,所以并没有问题。
var a = "hello world";
var b = 10;
switch (true) { // 匹配true
case (a || b == 10): // 这里的结果是"hello world",不是true
// ...
break;
default:
// ...
}