ecma262 中的赋值表达式

assignment expression in ecma262

考虑以下代码

var myVar = 'Hola';
{
  let myVar;
  myVar = 'Hello'
}

在第 4 行(myVar = 'Hello')我们使用赋值运算符

现在,当我在 Assignment Operators Evaluation

中查看 ecma262 时

表示赋值运算符左边是LeftHandSideExpression,右边是AssignmentExpression

换句话说看起来像那样

LeftHandSideExpression = AssignmentExpression

任何人都可以向我解释 myVar 将如何评估吗? 如果它应该是 LeftHandSideExpression ?

在块语句中,所有定义的变量都是局部变量,因此块中的 myVar 将是 "Hello"。在外部,用 var 定义的变量是全局变量,它的值永远不会改变,因为内部 myVar 是一个完全不同的变量,只是名称相同。所以用 var 定义的外部 myVar 从未改变其初始值 "Hola".

var myVar = "Hola";
{
  let myVar;
  myVar = "Hello";
  console.log("inside:", myVar);
}
console.log("outside:", myVar);

您可以在左侧放置不同的东西,例如:

a = 'foo'
a.b = ['foo','bar']
new Object().b = ['foo','bar']
foo().b = ['foo','bar']

对于 a = 的简单情况,不需要做太多事情。

但对于 a.b =new Object().b =foo().b =,您首先需要评估左侧。

要理解的主要部分是 evaluate 在这种情况下意味着我们 运行 这些 Runtime Semantics: Evaluation 部分用于语言中的各种语法部分。在

的情况下
myVar = 'Hello'

如果我们查看 13.15.2 Runtime Semantics: Evaluation 以评估 AssignmentExpression

1.a. Let lref be the result of evaluating LeftHandSideExpression.

将深入了解各个步骤,直到您最终得到 运行 13.1.3 Runtime Semantics: Evaluation,它定义了 myVar.

的评估行为

如果你逐步完成这个,关键是 lref 不会评估为 JS 值,它评估为 Reference Record 类型,这不是 JS 代码知道的值, 但表示可以分配值的位置的概念。对于您的代码段,它基本上是对变量列表所在范围和变量名称的引用,因此稍后执行赋值时,它会在稍后完全解析。

这是先评估左侧的行为,对于这样的示例更重要:

foo().bar = val();

因为评估左侧意味着 foo()val() 之前运行,这正是您所期望的。在这种情况下 foo() 运行,然后我们得到一个 Reference Recordfoo() 的 return 值作为赋值的目标,bar 作为 属性 要分配的名称。

回到13.15.2 Runtime Semantics: Evaluation,我们到达

1.e Perform ? PutValue(lref, rval).

这是最终分配发生的地方。 rval"Hello" 字符串本身,因为它已被评估。如果你看一下 6.2.4.6 PutValue ( V, W ),你可能会猜到第 3 部分是我们最终得到的变量赋值

  • 6.a。假设 base 为 V.[[Base]].
  • 6.b。断言:base 是一个环境记录。
  • 6.c。 Return ? base.SetMutableBinding(V.[[ReferencedName]], W, V.[[Strict]])

所以在你的情况下是

(<scope with closest `var/let myVar`).SetMutableBinding("myVar", "Hello", false)