Reassignment, mutation, reference types, and value

2019-06-05 21:10发布

问题:

How would you appropriately explain why these two examples differ?

// Reassignment
let a = 1;
let b = a;
a = 2;
console.log(b); // → 1

// Mutation
let myArray = [1, 2, 3];
let ourArray = myArray;
ourArray[2] = 10;
console.log(myArray); // → [1, 2, 10]

In regards to why the variable label behaves differently between the two, many resources claim that it's because of the difference between reference types and value types. But, doesn't that only apply to the differences between the values themselves?

When explicitly talking about just the variable label (i.e. why the labels in the first example do not track changes to the value, whereas it does in the second example), doesn't the true difference have to do with mutation vs. reassignment?

After all, we could just as easily reassign either myArray or ourArray and neither of them would have any sort of ongoing link. And, if primitive values were theoretically mutable in JavaScript, the first example could be made to behave just like the second.

Followup Question

I've also read conflicting information on whether or not primitive values only exist once in memory. If the number one, for example, only exists once in memory, then it would be appropriate to visualize variable labels as nametags, with every nametag that references the number one being 'stuck' to the same number one, right?

Again, if the number one were able to be mutated, then every nametag would track the same changes to that number one. But since one is immutable, nametags can either continue referencing the same number one, or they can be taken off and stuck to a different value (reassigned).

Am I on the right track here?

回答1:

You mix implementation details ("exist once in memory") with evaluation strategies ("reference types and value types") and specific expressions ("mutation vs. reassignment") in your question. Implementation details vary among various browsers and are likely to change in the future.

There are two evaluation strategies in Javascript:

  • Copy by value (pass by value when calling functions)
  • Copy by reference (pass by reference when calling functions)

Value Types

Value types are always copied by value. Assumed strings were mutable in Javascript:

let s = "abc";
let t = s;

s[0] = "x";
s; // "xbc";
t; // "abc";

Variables (or identifiers or name bindings) that are based on value types directly contain their values. If such a mutable string is mutated, then only the value of the corresponding identifier would be affected.

Unlike strings, numbers are atomic. For atomic primitives there is no practical distinction of mutation and reassignment:

let n = 0;
n = 1, n++, n = n + 1; // all reassignments

Reference types

A variable holding a reference type contains merely the reference to this type, which is a complex value (object) in Javascript. If two variables reference the same object, they each hold copies of this reference, not an direct alias. Hence, if one of the two variables is reassigned, this doesn't affect the other.

Reference types have identity, that is, their identity is no longer tied to values:

let xs = [1];
xs === [1]; // false


回答2:

When explicitly talking about just the variable label, doesn't the true difference have to do with mutation vs. reassignment?

Sure. I assume you mean that one mutates objects and assigns variables. But you can't always distinguish them like that. I could also say that I assign to object properties (in your second example) or mutate "variable-labelled" memory cells (in your first example). Is there really a difference?

But indeed, the variable (the label, the memory cell, the register) does always work the same, regardless whether it holds a primitive value or a reference value.

I keep reading conflicting information on whether or not primitive values only exist once in memory.

Well, the point is that it doesn't matter since primitive values are (by definition) immutable. A JS implementation can copy them around, or share references, the behaviour is not distinguishable by the script.

In the real world, numbers and booleans are usually copied while strings are typically interned (shared).