2

consider the following code

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

on line 4(myVar = 'Hello') we are using the Assignment operator

Now when i was looking at ecma262 in Assignment Operators Evaluation

it says that the left side in assignment operators is LeftHandSideExpression and the right side is AssignmentExpression

in other words it looks like that

LeftHandSideExpression = AssignmentExpression

can anyone explain to me how myVar is going to get evaluated ? if it's supposed to be LeftHandSideExpression ?

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
AngryJohn
  • 576
  • 4
  • 10

3 Answers3

3

The main piece to understand is that evaluate in this context means we're running these Runtime Semantics: Evaluation sections for the various grammar pieces in the language. In the case of

myVar = 'Hello'

if we look at 13.15.2 Runtime Semantics: Evaluation for the evaluation of AssignmentExpression the line

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

will drill down through the various steps until you eventually end up running 13.1.3 Runtime Semantics: Evaluation which defines the evaluation behavior of myVar.

The key thing if you step through this is that lref does not evaluation to a JS value, it evaluates to a Reference Record type, which isn't a value JS code is aware of, but represents the concept of a location where a value can be assigned. In the case of your snippet, it's basically a reference to the scope where the variable list, and the name of the variable, so when assignment is performed later, it is fully resolves later.

This is behavior of evaluating the left side first more important for examples like this:

foo().bar = val();

because evaluating the left means that foo() runs before val(), which is what you'd expect. In this case foo() runs and then we get a Reference Record with the return value of foo() as the target of the assignment, and bar as the property name to assign.

Going back to 13.15.2 Runtime Semantics: Evaluation, we get to

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

which is where the final assignment happens. rval is the "Hello" string itself since it has been evaluated. If you take a look at 6.2.4.6 PutValue ( V, W ), you can probably guess that the 3rd section is what we end up with for a variable assignment

  • 6.a. Let base be V.[[Base]].
  • 6.b. Assert: base is an Environment Record.
  • 6.c. Return ? base.SetMutableBinding(V.[[ReferencedName]], W, V.[[Strict]])

so in you're case it's

(<scope with closest `var/let myVar`).SetMutableBinding("myVar", "Hello", false)
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • from what i understand LeftHandSideExpression can be many things it can be identifierReference it can also be the `this` keyword in the case of `myVar = 'Hello'` it's an identifierReference right ? therefore the line you mentioned "1.a. Let lref be the result of evaluating LeftHandSideExpression." is actually doing the identifierReference evaluation which is this "1. Return ? ResolveBinding(StringValue of Identifier)." right ? and from that we get the Reference Record – AngryJohn Oct 11 '21 at 16:45
  • Yes it's specifically `myVar = 'Hello';` that uses `IdentifierReference`. The exact behavior depends on the syntax that is on the right, but they'll all evaluate to a `Reference Record` eventually. – loganfsmyth Oct 11 '21 at 17:20
2

Within the block statement, all defined variables are local, so myVar within the block will be "Hello". Outside, the variable defined with var is global, and its value never changed as the inner myVar was a totally different variable, just with the same name. So the outer myVar defined with var never changed from its initial value of "Hola".

var myVar = "Hola";
{
  let myVar;
  myVar = "Hello";
  console.log("inside:", myVar);
}
console.log("outside:", myVar);
Jacob
  • 1,577
  • 8
  • 24
1

You can have different things on the left-hand side like:

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

For the simple case of a = there is not much that has to be done.

But for a.b =, new Object().b = and foo().b = you first need to evaluate the left hand side.

t.niese
  • 39,256
  • 9
  • 74
  • 101