什么是表达式语句

在语句部分学习到了很多种语句类型,而实际上真正能干活的最终产生执行效果的语句不多(表达式语句真正的能干活),其它的各种语句基本上都是产生各种结构来控制表达式语句执行,或则改变表达式语句的意义。

什么是表达式语句

表达式语句实际上就是一个表达式,它是由运算符连接变量或者直接量构成的;一般来说,表达式语句要么是函数调用,要么是赋值,要么是自增自减,否则表达式的计算结果没有任何意义;但是从语法上是没有任何限制的,任何合法的表达式都可以当做表达式语句使用,如下:

1
a + b;

这句代码执行了 a 和 b 相加的值,但是不会显示出来,也不会产生任何执行效果(除非 a 和 b 是 getter),但是不妨碍它们符合语法也能够被执行。
下面学习一下都有哪些表达式(粒度从小到大):

  • PrimaryExpression 主要表达式
  • MemberExpression 成员表达式
  • NewExpression New 表达式
  • CallExpression 函数调用表达式
  • LeftHandSideExpression 左值表达式
  • AssignmentExpression 赋值表达式
  • Expression 表达式

PrimaryExpression 主要表达式

PrimaryExpression 它是表达式的最小单位,它所设计的语法结构是优先级最高的。
PrimaryExpression 包含了各种“直接量”(某种语法写出来的具有特定类型的值),如下一些基本类型的直接量:

  • “123” 字符串直接量
  • 123 数字直接量
  • null null 直接量
  • true/false boolean 直接量

除此之外,JavaScript 还能够用直接量的形式定义对象,针对函数、类、数组、正则表达式等特殊对象类型;如下:

1
2
3
4
5
({});
(function() {});
(class {});
[];
/abc/g;

需要注意的是,以 function、{ 和 class 开头的表达式语句与声明语句有语法冲突,因此,想要使用这样的表达式,必须用括号来回避语法冲突。

PrimaryExpression 还可以是 this 或者变量,在语法上,把变量称作“标识符引用”;

1
2
this;
myVar;

而任何表达式加上圆括号,也都被认为是 PrimaryExpression,这个机制使得圆括号称为改变运算符计算优先顺序的手段。

1
a + b;

这里就是 PrimaryExpression 的几种形式,下面学习由 PrimaryExpression 构成的复杂表达式:MemberExpression。

MemberExpression 成员表达式

MemberExpression 通常是用于访问对象成员的,有以下几种形式:

1
2
3
4
a.b;
a['b'];
new.target; // 必须是这个形式,它是新加入的语法,用于判断函数是否是被 new 调用
super.b; // super 是构造函数中用于访问父类的属性的语法

MemberExpression 最初设计是为了属性访问的,不过由于语法结构需要,下面的两种情况在 JavaScript 标准中也被当做 MemberExpression;

1
f`a${b}c`;

这是一个函数模板,这个带函数名的模板表示把模板的各个部分计算好后传递给一个函数。

1
new Cls();

另一个是带参数列表的 new 运算(注意,不带参数列表的 new 运算优先级更低,不属于 MemberExpression)。
后面这两种被放入 MemberExpression 仅仅意味着它们跟属性运算属于同一优先级,没有其他任何语义上的关联。

NewExpression New 表达式

MemberExpression 加上 new 就是 NewExpression;当然,不加 new 也可以构成 NewExpression,JavaScript 中默认独立的高优先级表达式都可以构成低优先级表达式。
注意,这里的 NewExpression 特指没有参数列表的表达式。

CallExpression 函数调用表达式

除了 NewExpression,MemberExpression 还能构成 CallExpression;它的基本形式是 MemberExpression 后加一个括号里的参数列表,或者直接用上 super 关键字代替 MemberExpression。

1
2
a.b(c);
super();

看起来其实很简单,但是它有一些变体,如下:

1
2
3
4
a.b(c)(d)(e);
a.b(c)[3];
a.b(c).d;
a.b(c)`xyz`;

这些变体形态,跟 MemberExpression 几乎一一对应;实际上就可以理解为 MemberExpression 中的某一子结构具有函数调用,那么整个表达式就成为了一个 CallExpression;
而 CallExpression 就失去了比 NewExpression 优先级高的特性。

LeftHandSideExpression 左值表达式

NewExpression 和 CallExpression 统称为 LeftHandSideExpression,左值表达式。
直观的讲,左值表达式就是可以放到等号左边的表达式,如下:

1
a() = b;

这样的写法其实是符合语法的,只是原生的 JavaScript 函数返回的值都不能被赋值。因此,多数时候我们看到的赋值将会是 CallExpression 的其他形式;如下:

1
a().c = b;

另外,根据 JavaScript 运行时的设计,某些宿主会提供返回引用类型的函数,这时候赋值就是有效的了。
而左值表达式的常用用法就是用于构成赋值表达式。

AssignmentExpression 赋值表达式

AssignmentExpression 赋值表达式也有多种形态,如下:

  • a = b;
  • a = b = c = d; // 等价于(a = (b = (c = d)));也就是说先把 d 赋值给 c,再把整个表达式结果赋值给 b,最后赋值给 a

除此之外,赋值表达式还可以结合一些运算符,如下:

  • a += b; // 等价于 a = a + b;
  • a -= b; // 等价于 a = a - b;
  • a = b; // 等价于 a = a b;
  • a /= b; // 等价于 a = a / b;
  • a %= b; // 等价于 a = a % b;
  • a <<= b;// 等价于 a = a << b;
  • a >>= b;// 等价于 a = a >> b;
  • a >>>= b; // 等价于 a = a >>> b;
  • a &= b; // 等价于 a = a & b;
  • a ^= b; // 等价于 a = a ^ b;
  • a |= b; // 等价于 a = a | b;
  • a = b; // 等价于 a = a b;

在这里,赋值表达式的等号左边和右边能用的表达式类型不一样,这里学习的就是表达式左边的部分(左值表达式)的语法结构,后面再学表达式右边的部分。

Expression 表达式

赋值表达式可以构成 Expression 表达式的一部分,在 JavaScript 中,表达式就是用逗号运算符连接的赋值表达式。

而 JavaScript 中,比赋值运算符优先级更低的就是逗号运算符,如下:

1
(a = b), (b = 1), null;

逗号分隔的表达式会顺次执行。

总结

这里对赋值表达式和赋值表达式左边的部分进行了学习,加深了对表达式的认识。