JS 代码块的作用

817 阅读5分钟

之前开启了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 ExpressionsLeft-Hand-Side ExpressionsPostfix ExpressionsUnary OperatorsMultiplicative OperatorsAdditive OperatorsBitwise Shift OperatorsRelational OperatorsEquality OperatorsBinary Bitwise OperatorsBinary Logical OperatorsConditional Operator ( ? : )Assignment OperatorsComma Operator ( , )

语句包括:BlockVariableStatementEmptyStatementExpressionStatementIfStatementIterationStatementContinueStatementBreakStatementReturnStatementWithStatementLabelledStatementSwitchStatementThrowStatementTryStatementDebuggerStatement

接下里找几个重点分析的对象:

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:

  1. Let lref be the result of evaluating Expression.
  2. Call GetValue(lref). // 计算左值
  3. Let rref be the result of evaluating AssignmentExpression.
  4. Return GetValue(rref). // 返回右值

Comma Operator描述表明:Comma是js的操作符,由comma进行的运算会调用左值,返回右值。所以能解释这个现象:

> let a = (console.log('leftValue'), 'a')
VM657:1 leftValue
>console.log(a)
"a"

总结statement

  1. statement和expression存在交集。
  2. 观察规范中对expression的描述可以发现每一种expression都会有一个ReturnValue。
  3. 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>

图示

image.png

参考文献

致谢

感谢无数位在技术的道路上孜孜不倦的具有钉子精神的作者。没有你们的努力作为铺垫我无法完成此文。 感谢郑州大学的某位教授,没有他的启蒙和指导,我就无法尽情地畅游在JS的世界中,它是具有大师精神的老师,具有钉子精神的工匠。谢谢您,X老师。