Home > Software design >  Why does a variable retain the initial value of the variable to which it was first assigned even aft
Why does a variable retain the initial value of the variable to which it was first assigned even aft

Time:11-05

In the first example, it is understood that num2 gets the updated value of num1 i.e., 4.

let num1 = 2;
num1 = 4;
let num2 = num1;
console.log(num1);  // result 4
console.log(num2);  // result 4

In the second example, why doesn't lastName get the updated value of ``firstName` i.e, "Henry".

let firstName = "David";
let lastName = firstName;
firstName = "Henry";
console.log(firstName); // result Henry
console.log(lastName);  // result David

Is it due to the order of declaration?

CodePudding user response:

It's not the order of declaration, it's the order of assignment.

num2 = num1; copies the value from num1 to num2. Now they both contain that same value. There is no ongoing link between them, they just have the same value in them. In your first example, you're looking at the values immediately after assigning to num2, so of course you see the same thing.

let lastName = firstName; also copies the value from firstName to lastName, and there's also no ongoing link between them, they just contain the same value. But then you change the value in firstName before looking at them. That just changes the value in firstName, it has no effect whatsoever on lastName.

Let's go through your second example step by step:

  1. The declarations are processed upon entry to the scope where the code appears, creating the variables (though not initializing them; if you tried to use them without assigning to them, you'd get an error). That gives us something like this in memory:

    firstName: <uninitialized>
    lastName: <uninitialized>
    

    Now step-by-step code execution starts:

  2. The initializer firstName = "David" is executed. Now we have:

    firstName: "David"
    lastName: <uninitialized>
    
  3. The initializer lastName = firstName is executed. Now we have:

    firstName: "David"
    lastName: "David"
    

    Note that the two variables don't refer to each other in any way. They just have the same value in them.

  4. Now the firstName = "Henry" assignment is executed, and we get:

    firstName: "Henry"
    lastName: "David"
    

    That assignment has no effect on lastName because there is no link between the variables.


Your examples both use primitives (numbers and strings in your case; strings are primitives in JavaScript in the normal case, though in some other languages they're objects [and they can be in JavaScript as well, it's just unusual]). At some point, someone's going to tell you it's different for objects. They're mistaken, it's exactly the same for objects. It's just the value stored in the variables isn't the object, it's a reference to the object saying where it is elsewhere in memory. Think of that reference like a number indexing into a block of memory. That's not exactly true, but it's a good mental model. Let's look at that:

let a = {name: "Henry"};
let b = a;
console.log(a.name); // "Henry"
console.log(b.name); // "Henry"
a = {name: "David"};
console.log(a.name); // "David"
console.log(b.name); // "Henry"

That's exactly the same thing you did with primitives, using objects. Let's walk through it:

  1. The declarations are processed upon entry to the scope:

    a: <uninitialized>
    b: <uninitialized>
    

    Now step-by-step code execution starts:

  2. The initializer a = {name: "Henry"} is executed. Now we have:

                                    −−−−−−−−−−−−−−− 
    a: Ref45132−−−−−−−−−−−−−−−−−−−>|   (object)    |
    b: <uninitialized>              −−−−−−−−−−−−−−− 
                                   | name: "Henry" |
                                    −−−−−−−−−−−−−−− 
    

    Notice how the value in a isn't the actual object, it's something saying where that object is, elsewhere in memory. (I've shown it as Ref45132, but that's just so I have something to show, you can never actually observe the values of references in JavaScript).

  3. The initializer b = a is executed. Now we have:

    a: Ref45132−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 
                                             |
    b: Ref45132−−−−−−−−−−−−−−−−−−−−−−−−−−    |
                                         |   |
                                         v   v
                                    −−−−−−−−−−−−−−− 
                                   |   (object)    |
                                    −−−−−−−−−−−−−−− 
                                   | name: "Henry" |
                                    −−−−−−−−−−−−−−− 
    

    The variables don't refer to each other in any way, but they both have the same value in them, and that value says where the object is. So they both indirectly refer to the same object.

  4. The first console.log(a.name) and console.log(b.name) are each executed. Since the variables refer to the same object, we get the same name regardless of which variable we use.

    Henry
    Henry
    
  5. Now the a.name = {name: "David"}; assignment is executed. This changes the value in a (let's say it changes from Ref45132 to Ref91324), making it refer to a different object, so we have:

                                              −−−−−−−−−−−−−−− 
    a: Ref91324−−−−−−−−−−−−−−−−−−−−−−−−−−−−−>|   (object)    |
                                              −−−−−−−−−−−−−−−  
    b: Ref45132−−−−−−−−−−−−−−−−−−−−−−−−−−    | name: "David" | 
                                         |    −−−−−−−−−−−−−−−  
                                         v    
                                    −−−−−−−−−−−−−−− 
                                   |   (object)    |
                                    −−−−−−−−−−−−−−− 
                                   | name: "Henry" |
                                    −−−−−−−−−−−−−−− 
    
  6. The second console.log(a.name) and console.log(b.name) are each executed. Since the variables refer to different objects, and the objects have different values for their name properties, we get different names.

    David
    Henry
    

This next bit is where people get confused and want to say it's different with objects, let's look at doing something different after logging the names the first time:

let a = {name: "Henry"};
let b = a;
console.log(a.name); // "Henry"
console.log(b.name); // "Henry"
a.name = "David";    // <=================== change is here
console.log(a.name); // "David"
console.log(b.name); // "David"

That's very different from the previous example with a = {name: "David"} (and very different from your examples with primitives) and as you can see the result is different. So what happened? Why did changing a.name change b.name? Is there some link between a and b? No, there isn't. It's just that they have the same value in them, and that value says where the object is, independent of the variables. Although we're using a to get to that object, it's not a we're changing, it's the object we're changing. Since it's the object that changes, we see that change if we get to the object another way (through b, for instance).

Let's walk through it:

  1. The declarations are processed upon entry to the scope:

    a: <uninitialized>
    b: <uninitialized>
    

    Now step-by-step code execution starts:

  2. The initializer a = {name: "Henry"} is executed. Now we have:

                                    −−−−−−−−−−−−−−− 
    a: Ref45132−−−−−−−−−−−−−−−−−−−>|   (object)    |
    b: <uninitialized>              −−−−−−−−−−−−−−− 
                                   | name: "Henry" |
                                    −−−−−−−−−−−−−−− 
    

    Notice how the value in a isn't the actual object, it's something saying where that object is, elsewhere in memory. (I've shown it as Ref45132, but that's just so I have something to show, you can never actually observe the values of references in JavaScript).

  3. The initializer b = a is executed. Now we have:

    a: Ref45132−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 
                                             |
    b: Ref45132−−−−−−−−−−−−−−−−−−−−−−−−−−    |
                                         |   |
                                         v   v
                                    −−−−−−−−−−−−−−− 
                                   |   (object)    |
                                    −−−−−−−−−−−−−−− 
                                   | name: "Henry" |
                                    −−−−−−−−−−−−−−− 
    

    The variables still don't refer to each other in any way, but they both have the same value in them, and that value says where the object is. So they both indirectly refer to the same object.

  4. The first console.log(a.name) and console.log(b.name) are each executed. They both refer to the same object, so we get the same name:

    Henry
    Henry
    
  5. Now the a.name = "David"; assignment is executed. This changes the object, it doesn't change a. a is completely unaffected by it, as is b. But the object they both refer to is updated, as we can see here:

    a: Ref45132−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 
                                             |
    b: Ref45132−−−−−−−−−−−−−−−−−−−−−−−−−−    |
                                         |   |
                                         v   v
                                    −−−−−−−−−−−−−−− 
                                   |   (object)    |
                                    −−−−−−−−−−−−−−− 
                                   | name: "David" |
                                    −−−−−−−−−−−−−−− 
    

    Since they both refer to the same object, we see the same name:

    David
    David
    

The distinction is between a = {name: "David"} and a.name = "David". The former creates a new object and updates the value in a to refer to it. The latter doesn't change a at all, it just changes the object that a refers to.

CodePudding user response:

This is variable assignment (you tagged it).

In let lastName = firstName, lastName is not any kind of alias to firstName but takes its value (at the time you execute this instruction) from the other variable firstName. After that, variables have their own life.

  • Related