之前开启了Vue源码阅读的过程,期间发现源码部分代码是使用 {} 包括的。一直有这个疑问——为什么要使用 {} 将代码包含起来?今天就从底层上好好来研究一下。
本文结论
JS block的作用:
- 延长作用域
- 完整执行statements
研究问题的思路
偶然在揭秘命名表达式中发现一句话:在使用eval对JSON进行执行的时候,JSON字符串通常被包含在一个圆括号里:eval('(' + json + ')'),这样做的原因就是因为分组操作符,也就是这对括号,会让解析器强制将JSON的花括号解析成表达式而不是代码块。——> () 是分组操作符,那么 {} 是什么呢?以此为入口进入 {} 的世界。
什么是block?
规范中的定义:
Block :
{ StatementList(opt) }
StatementList :
Statement
StatementList Statement
该定义解释了Block是statement的集合。
那么什么是statement呢?
什么是statement?
这里只是因为block是由statement组成,所以准备dive into details。如果只对 {} 感兴趣的话,大可跳过这一部分
Expression statements
An expression on its own is also a statement.
statement和expression的区别from Expressions versus statements in JavaScript
表达式和语句之间又是有区别的。
表达式会返回一个值。可以在任何需要值的地方使用表达式。例如:在函数对象的形参列表中使用表达式。
expression example:
- myvar
- 3 + x
- myfunc("a", "b")
语句可以被粗略地描述为一个行为,例如循环、判断等对应着循环语句、判断语句等。程序基本上是一系列语句的结合。无论何时,当JavaScript需要编写一条语句时,均可以写入一个表达式。这样的语句称为表达式语句expression statement)。但是反之并不成立,你不能编写一条语句来代替表达式。例如:if语句不能成为函数的参数。
statement example:
var x;
var y = 0;
if (y >= 0){
x = y;
} else {
x = -y;
}
综上所述,语句包含表达式~
statement和expression的区别from 英文释义
expression: a word or phrase, especially an idiomatic one, used to convey an idea.
statement: a definite or clear expression of something in speech or writing.
这就是我对statement和expression迷惑的地方,从英语语义中就可以看出来statement is expression。所以从这个出发点来讲,上面分析的结果——语句包含表达式,是对的,它们之间是由交集的。
OK,let's dive into statement and expression in EcmaScript
表达式包括:Primary Expressions、Left-Hand-Side Expressions、Postfix Expressions、Unary Operators、Multiplicative Operators、Additive Operators、Bitwise Shift Operators、Relational Operators、Equality Operators、Binary Bitwise Operators、Binary Logical Operators、Conditional Operator ( ? : )、Assignment Operators、Comma Operator ( , )
语句包括:Block、VariableStatement、EmptyStatement、ExpressionStatement、IfStatement、IterationStatement、ContinueStatement、BreakStatement、ReturnStatement、WithStatement、LabelledStatement、SwitchStatement、ThrowStatement、TryStatement、DebuggerStatement
接下里找几个重点分析的对象:
Statements
Syntax
Statement :
Block
Statements描述说明 block is statement。再结合上文中block定义,可以得到block的一个用处是将statements转化为statement,即将多个statement合并为一个statement。所以可以解释下例:if和for语句中为什么只有一个statement的时候我们可以省略{}。
if (a === 1)
console.log('hello')
规范中有描述Conditional Operator ( ? : ) is a statement。所以也能解释为什么下例中的语句等价。
var x;
if (y >= 0) {
x = y; <====等价于====> var x = (y >= 0 ? y : -y);
} else {
x = -y;
}
The Grouping Operator in Primary Expressions
( Expression ) is the production PrimaryExpression // 说明()内的是表达式
The Object Initialiser in Primary Expressions
An object initialiser is an expression describing the initialisation of an Object, written in a form resembling a literal.
Syntax
ObjectLiteral :
{ } // 结合block定义说明{}内的不一定是statement,可能是expression
{ PropertyNameAndValueList }
{ PropertyNameAndValueList , }
The Grouping Operator in Primary Expressions + The Object Initialiser in Primary Expressions + Statements + Block的描述可以解释下例:
> eval("{ foo: 123 }") // 当做statement处理
123
> eval("({ foo: 123 })") // 当做expression处理
{ foo: 123 }
> eval("{ foo: 123; bar: 234 }") // 当做语句处理
234
> eval("{ foo: 123, bar: 234 }") // 明明人家当做statement处理,你非要写成expression的形式
VM593:1 Uncaught SyntaxError: Unexpected token ':'
at <anonymous>:1:1
Comma Operator ( , )
Syntax
Expression :
AssignmentExpression
Expression , AssignmentExpression
this Expression is evaluated as follows:
- Let lref be the result of evaluating Expression.
- Call GetValue(lref). // 计算左值
- Let rref be the result of evaluating AssignmentExpression.
- Return GetValue(rref). // 返回右值
Comma Operator描述表明:Comma是js的操作符,由comma进行的运算会调用左值,返回右值。所以能解释这个现象:
> let a = (console.log('leftValue'), 'a')
VM657:1 leftValue
>console.log(a)
"a"
总结statement:
- statement和expression存在交集。
- 观察规范中对expression的描述可以发现每一种expression都会有一个ReturnValue。
- block可以合并多个statement为一个statement。
所以block能够合并多个statement为一个statement的作用是什么呢?我们来看一段描述:
if (you are a student) {
write your homework;
hand in homework;
}
规定中对if语句的描述是:
if ( Expression ) Statement else Statement
if ( Expression )后面需要的是statement而不是statements(这样做是为了让编译器能够保证语言不出现二义性),如果没有{}的存在,怎样既实现write your homework又实现hand in homework呢?所以{}保证一组语句它们要么都执行,要么都不执行。
关于block作用域
block能够延长作用域。
代码
<html>
<body>
<div>
block{}
</div>
<script>
var outer1 = 'outer1';
{
var block1 = 'block1'
let block2 = 'block2'
}
console.log(block1);
</script>
</body>
</html>
图示
参考文献
- [1] Expressions versus statements in JavaScript
- [2] JavaScript Statements
- [3] ECMASCRIPT 5th
- [4] 揭秘命名函数表达式
致谢
感谢无数位在技术的道路上孜孜不倦的具有钉子精神的作者。没有你们的努力作为铺垫我无法完成此文。 感谢郑州大学的某位教授,没有他的启蒙和指导,我就无法尽情地畅游在JS的世界中,它是具有大师精神的老师,具有钉子精神的工匠。谢谢您,X老师。