0

I've heard multiple explanations what the idea behind assignment in ECMAScript specification is. But which one does the EMCAScript spec actually apply? Let's take a look at a simple practical example:

  1. Assignment by copying values, aka "the classic C++/Java approach".

    let a = 4 // assign value 4 to variable a
    let b = a // copy value of a into b
    a = 5 // reassign a with value 5
    
  2. Assignment by sharing a reference. Primitive values are immutable, Objects are mutable.

    let a = 4 // Assigns a with a reference to the immutable value 4
    let b = a // copies the reference of a to b
    a = 5 // reassigns a with a reference to 5 (in-place mutation would cause b to change as well)
    

Arguments for Variant 1:

Most importantly, ECMAScript itself seems to specify the assignment as copying values.

https://tc39.es/ecma262/#sec-declarative-environment-records-setmutablebinding-n-v-s

 AssignmentExpression : LeftHandSideExpression = AssignmentExpression

1. If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then
    a. Let lref be ? Evaluation of LeftHandSideExpression.
    b. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
        i. Let rval be ? NamedEvaluation of AssignmentExpression with argument lref.[[ReferencedName]].
    c. Else,
        i. Let rref be ? Evaluation of AssignmentExpression.
        ii. Let rval be ? GetValue(rref).
    d. Perform ? PutValue(lref, rval).
    e. Return rval.
2. Let assignmentPattern be the AssignmentPattern that is covered by LeftHandSideExpression.
3. Let rref be ? Evaluation of AssignmentExpression.
4. Let rval be ? GetValue(rref).
5. Perform ? DestructuringAssignmentEvaluation of assignmentPattern with argument rval.
6. Return rval.

Branch 1d is particularly interesting for primitives, which ultimately leads to the SetMutableBinding method of the EnvironmentRecord, which states:

https://tc39.es/ecma262/#sec-declarative-environment-records-setmutablebinding-n-v-s

It attempts to change the bound value of the current binding of the identifier whose name is N to the value V.

Arguments for Variant 2:

On the other hand a lot of documentation suggests that example 1 might be the case. Like the explicit mentioning of immutability of primitive values, which only really makes sense if there would be a possibility to affect other variables by doing so (by having multiple references point to that value).

https://developer.mozilla.org/en-US/docs/Glossary/Primitive?retiredLocale=de

All primitives are immutable; that is, they cannot be altered. It is important not to confuse a primitive itself with a variable assigned a primitive value. The variable may be reassigned to a new value, but the existing value can not be changed in the ways that objects, arrays, and functions can be altered.

This explanation in Eloquent JavaScript I assume also talks about a reference (binding) pointing to a different value.

https://eloquentjavascript.net/04_data.html

Even though number values don’t change, you can use a let binding to keep track of a changing number by changing the value the binding points at.

My guess is, the specification is rather general. Which applies better to variant 1, but it is free to the implementor to implement it the way you want. Is that correct?

tweekz
  • 187
  • 1
  • 9
  • The first two lines aren't assignments at all, they're variable initialisers :-) – Bergi Jan 29 '23 at 08:49
  • Hi @Bergi. Isn't it an initialization with assignment alltogether? Anyhow, what about the assignment itself? – tweekz Jan 29 '23 at 08:53
  • I don't understand the question. Are you really asking whether `b = a` creates a reference to variable `a`? A simple `console.log(b)` will answer that: no, the value is copied. Or are yu asking how this described in the specification? – Bergi Jan 29 '23 at 08:53
  • I know that it is of course copy by value. I just want to know if the spec also treats it as copying values (which seems the case by the cited spec page), or if it is treated as a reference to an immutable value object, which is shared until a writing action occurs. – tweekz Jan 29 '23 at 08:56
  • Not sure what you mean by "*shared until a writing action occurs*". The primitive value/reference is immutable, as you say yourself, there is no operation that changes (writes to) it. – Bergi Jan 29 '23 at 08:59
  • "by value" could be achieved by pointers to (primitive) value objects that are shared until you change the value, then it is reassigned to a new value object. The SetMutableBinding method however does not dictate anything of that matter, just that the value of the binding gets changed. As it does not say how, I guess it could as well be a in-place mutation? – tweekz Jan 29 '23 at 09:07
  • Yes, primitives can be implemented as pointers (strings more or less necessarily are), and copying the pointer shares the referenced value. However, that has nothing to do with *SetMutableBinding*, which is about writing a value into a variable. It does not write into the primitive (pointer) value, which is impossible since they're immutable. – Bergi Jan 29 '23 at 09:11
  • That's exactly where I fail to understand the concept. What value does SetMutableBinding change and how? – tweekz Jan 29 '23 at 09:22
  • 1
    It changes the **variable** from one value to another value. Whether those values are the two numbers "four" and "five" themselves, or two references to numbers stored elsewhere, does not matter to it. – Bergi Jan 29 '23 at 09:28
  • Btw, re "*I've heard multiple explanations what the idea behind assignment is.*": both explanations you quoted say the same thing. And neither talks about whether primitives are implemented as pointers or not. – Bergi Jan 29 '23 at 09:32
  • OK, I think I'm slowly getting there. So the spec does not forbid to directly write a value (non-reference) into a variable? I am asking, cause I find that mental model way easier to picture. – tweekz Jan 29 '23 at 09:37
  • Yes. On the contrary, the spec *requires* writing values into variables, that's what `Put` does. – Bergi Jan 29 '23 at 09:50
  • Maybe you're confused by "*change the bound value of the current binding*". That refers to changing the ("value attribute" of) the binding, not to mutating (modifying) the value itself. – Bergi Jan 29 '23 at 09:52
  • I think the key thing to understand is that referencing or assigning to a variable works exactly the same way regardless of the values that are involved or how values are represented ('value types' vs 'reference types'). As such you can easily verify that (2) is not how JavaScript works: `let a = 4; let b = a; a = 5; console.log(b);`. This Wikipedia article might also help: https://en.wikipedia.org/wiki/Evaluation_strategy – Felix Kling Jan 29 '23 at 09:58
  • How is that value stored inside the value attribute of the binding? – tweekz Jan 29 '23 at 10:17
  • 1
    @tweekz The value is stored like it is stored anywhere else. A [binding](https://stackoverflow.com/a/39259958/1048572) in a declarative environment record is really just a labelled memory cell. How values (both primitives and objects) are represented in memory is left up to the implementation. – Bergi Jan 29 '23 at 10:42
  • @Bergi OK, so the value might be stored as bits and bytes in the bindings attribute. But in that case, wouldn't changing the attributes value result in changing the value itself? – tweekz Jan 29 '23 at 10:53
  • 1
    @tweekz What's the difference between "changing a value" and "overwriting a value with a different value" in your book, then? – Bergi Jan 29 '23 at 10:55
  • Good point. :) So this comes down to interpretation basically? Like you could consider a in-memory representation of a value as a separate entity? – tweekz Jan 29 '23 at 10:58
  • @tweekz Kinda, but no. By definition, a [value](https://en.wikipedia.org/wiki/Value_(mathematics)) doesn't change. The number "four" is a different value than the number "five", you cannot change one into the other. What you can change is the content of a variable. – Bergi Jan 29 '23 at 11:04
  • One more question: If values directly stored in the value attribute of a binding are immutable by default anyway and the spec allows that representation, then why does MDN emphasize the immutability of primitives? Is it because they want to provide a common foundation for all possible impelementations (like referenced primitive values needing to be immutable)? Or the need of auto-wrapped primitives to be immutable ... – tweekz Feb 02 '23 at 11:53
  • Because not everyone comes from maths (or computer science) to know that definition of "value". MDN still needs to explain how JS works to people who don't read the specification. The distinction between immutable primitive values (that do not have a "content" or a "location") and object (values) that have an identity and mutable contents (properties) is a very important one to understand. – Bergi Feb 03 '23 at 00:30
  • When reading the spec, I didn't find a lot about mutability/immutability or how primitives and objects differ. Wouldn't it be a better distinction to outline that objects store a reference, whereas primitives store the value itself? – tweekz Feb 05 '23 at 14:12
  • @tweekz Again, primitives don't "store" anything. They *are* the value itself. But yes, [the spec is not explicit about this](https://stackoverflow.com/a/23556036/1048572), it just assumes the reader to be familiar with general computer science terminology. – Bergi Feb 05 '23 at 14:33
  • I gotta be more careful with terminology. Is it just me or is the MDN (and some other resources) written in a way, that suggest the value attribute of the binding to be holding a reference to the primitive value? – tweekz Feb 05 '23 at 19:33
  • 1
    I don't think that's suggested anywhere. One could however say that the whole binding itself is a reference to the value, or better, that the binding is a *referenceable* memory cell holding a value (i.e. the binding can be referenced by identifiers). – Bergi Feb 05 '23 at 20:04
  • Is that why they say [const creates a read-only reference to a value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const?retiredLocale=de#description)? May I ask what mental model you personally use when doing something like let a = 1 ? What do you picture that happens in memory? – tweekz Feb 06 '23 at 09:07

1 Answers1

0

Assignment always copies by value, you can easily check that option 2 is not what happens.

What may be sometimes confusing is that if you copy (by value) a pointer and then modify the value that it points to, then the value the original variable points to will always be modified (because they both point to the same memory address). And things like objects and arrays are secretly pointers.

let a = {x: 3};
let b = a;

b.x = 5;
// Now a.x is 5 as well because a and b point to the same memory address 

b = {x: 7};
// a.x is still 5 because we changed the value of b
Guillaume Brunerie
  • 4,676
  • 3
  • 24
  • 32