Travis Heeter
Travis Heeter

Reputation: 14074

Where does a variable point when I set it equal to a global variable?

Here's a simple example:

  1| window.gamelogic = {};
  2| var g = gamelogic;
  3| g.points = 1;
  4| g.array = ["foo","bar"];
  5| var b = g.points;
  6| b = b + 1;
  7| console.log(window.gamelogic);
  8| console.log(b);

This will print:

Object { points=1, array=[2] }
2

So there's 2 things to note here:

  1. A (seemingly local) variable - g - when set to a global object and updated, also updates the golbal object - window.gamelogic. (Updating g also updated window.gamelogic).

  2. A local int, b (set to a global int, points), does not update the global variable when it is changed. (updating b did not update window.gamelogic.points)

Based on the first point, one would think that when a var is pointed to a global object, you're actually just creating another pointer to the same memory location of that global object. This would explain why updating g also updates window.gamelogic.

However, b not updating window.gamelogic.points seems to counter this argument.

What is going on here?

Upvotes: 3

Views: 138

Answers (4)

T.J. Crowder
T.J. Crowder

Reputation: 1075079

In JavaScript, variables (and properties) contain values. Values can have many different types (number, string, boolean), one of which is object reference, which is a reference to an object but isn't the actual object itself. An easy way to think of an object reference is that it's just a number, like an index into a really big array, telling us where the object is. (That's not literally true, but it's a useful way to think of it.) Or in non-programming terms, Joe may have a piece of paper with "123 Any St." written on it, which is where Joe's house is. The paper is a variable (or property); the "123 Any St." is a value (an object reference, in this case), and the house is an object.

Objects are not values, and so they cannot be stored in variables or properties (or passed as function arguments). Only references to them can be.

When you assign a value to a variable or property (or pass it into a function as an argument), you're copying the value into it from the source. So a = b copies the value from b into a. When b contains an object reference, it's the reference, not the object, that gets copied; then a and b both refer to the same object. It's like Mary getting out a piece of paper (a) and copying down what's on Joe's piece of paper (b). Now both pieces of paper say where Joe's house is. The house isn't copied, just the information telling us where it is.

With that in mind, let's look at your code. When you do

window.gamelogic = {};

it creates an object and the copies its reference (a value) into the property gamelogic. Here's a rough sketch of what's in memory at that point, omitting a lot of unnecessary details:

                 +-------------------+
                 | (stuff omitted)   |       +-----------+
window:ref429--->| gamelogic: ref758 |------>|           |
                 +-------------------+       +-----------+

Then you do this:

var g = gamelogic;

which (waves hands) creates a variable (I'll explain the hand waving later) and assigns (copies) the value in gamelogic to it. Since that value is an object reference, g and gamelogic now point the same place; that is, they refer to the same object:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +-----------+
                                         +-->|           |
                                         |   +-----------+
g: ref758--------------------------------+

Then you do

g.points = 1;

which creates a property on that object called points and copies the value 1 into it:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +-----------+
                                         +-->| points: 1 |
                                         |   +-----------+
g: ref758--------------------------------+

Let's highlight what we've done here: We haven't changed the value in g in any way, it's still the same as it was: A reference to the object that gamelogic also references. What we've done is changed the state of that object (by adding a property to it). This is one of the key things about objects: They have state that can be changed. When that state is changed, it doesn't matter which copy of the reference to it you have when you look at it; you'll see the same object, with its (updated) state, regardless.

Okay, continuing:

g.array = ["foo","bar"];

which creates an array (which is an object), and creates a property called array on our object, and copies the value of the array's reference into the property:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +---------------+     +----------+
                                         +-->| points: 1     |     | 0: "foo" |
                                         |   | array: ref804 |---->| 1: "bar" |
g: ref758--------------------------------+   +---------------+     +----------+

Then you do:

var b = g.points;

which (waves hands) creates a variable and copies the value from g.points (1) into it:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +---------------+     +----------+
                                         +-->| points: 1     |     | 0: "foo" |
                                         |   | array: ref804 |---->| 1: "bar" |
g: ref758--------------------------------+   +---------------+     +----------+
b: 1

Then

b = b + 1;

gets the value 1 from b, adds 1 to it, and stores the new value (2) in b. g.points is completely unaffected:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +---------------+     +----------+
                                         +-->| points: 1     |     | 0: "foo" |
                                         |   | array: ref804 |---->| 1: "bar" |
g: ref758--------------------------------+   +---------------+     +----------+
b: 2

The key points above are:

  • Variables and properties (and function arguments) contain values.
  • A values has a type, such as string, number, boolean, or object reference.
  • An object reference is just a value that says where an object is.
  • Objects are not values; references to objects are values.
  • Objects have state that can be changed.*

(* If they allow it. It's possible to create an object that doesn't allow its state to be changed; those are called "immutable" objects. And it can be very, very handy and powerful to do that. In JavaScript, you do that with Object.freeze and similar, since by default objects are very loose and you can add properties to them just by assigning. In many other languages, it's more basic: You just don't define any public field that can be changed, and don't define any public methods that change the object's state.)


About that "creates a variable" hand-waving, I was ignoring two details there because they weren't important then:

  1. In JavaScript, var declarations are processed before the code starts running, so the variables g and b were both already created before the first line of your step-by-step code ran. Initially, the had the value undefined in them.

  2. Because you used var at global scope, b and g became properties of the global object, which is what window points to. In fact, window itself is a property of the global object. Up through ES5, all globals were properties of the global object. (In ES6/ES2015, we have a new category of globals that aren't: Ones created with let, const, or class.)

So technically, our first diagram should have looked like this:

+--------------------------+
|   +-------------------+  |
|   | (stuff omitted)   |  |
+-->| window: ref429    |--+    +-----------+
    | gamelogic: ref758 |------>|           |
    | g: undefined      |       +-----------+ 
    | b: undefined      |
    +-------------------+

...but, well, that seems like it would have been less than useful. :-)

Upvotes: 6

SergeyA
SergeyA

Reputation: 62603

The simple mental model to use with JavaScript is to forget what has been told in numerous books and accept the fact that JavaScript has pointers. While according to language specification JavaScript has no pointers, a traditional pointer model can be a useful way to to approach the language.

When thought like this, it has only pointers for objects and value types for primitives. When this model is used, there is no mystery whatsoever. Here is breakdown:

window.gamelogic = {}; // creates an object, stores *pointer* to the object in window.gamelogic (global)
var g = gamelogic; // creates an pointer g, which points to the same object as gamelogic
g.points = 1; // Access object by pointer, set's object value to 1
g.array = ["foo","bar"]; // same
var b = g.points; // g.points is a primitive, so b is a copy of g.points
b = b + 1; // b is incremented indepently of g.points

And the statement that JavaScript has references is simply confusing. It does not have references at all.

I will explain why I maintain that JavaScript has no references. First of all, let's define some basics. It is usually accepted that references are another name of the object, while pointers are independent objects which can be used to access the object they point to. For that matter, pointers do not have to have actual memory addresses in them. It is enough to that having a pointer allows one to access the object pointed by it, and that pointer itself is an object, which can be modified.

On the other hand, references are not indepented objects. Instead, they are aliases, or second names, for already existing object. A pointer can be changed and point to a different object. A reference can not - it will always be the second name for the object it was assigned to. The difference is subtle, but crucial.

Both pointers and references are what I can call 'an indirect access' type - that is, the type which allows indirect access to underlying object.

So, let's consider following example:

function foo(datum) {
    datum = datum2;
}

var dat = datum1;
// Initialize dat
foo(dat);
// what is dat now? (1)

What will happen within foo()? We all know that dat in the point (1) will still hold the value of datum1, despite seemingly being changed in foo(). We might assume the whole object (dat) is passed by value to foo() - that is, copied into independent object and give to foo() - and such operation preserved original dat from any modification.

However, as we all well know, if we are to modify foo as following:

function foo(datum) {
    datum.property = 42;
}

We will know that at point (1) dat.property is also 42. (Assume it was not set to this value before). That means, that the suggestion about whole dat object being passed to foo is wrong - modification of the copy would not affect the original. What we would also notice is that the original datum1 object also has the property set to 42 - which means that passing into functions are not really different from simple assignments. For the sake of simplicity, we can even remove the function call from the picture, and use following code:

var datum1 = Object();
var datum2 = Object();
// datum1 and datum2 are differently initialized
var dat = datum1;
dat = datum2; // datum1 and datum2 are unchanged
dat.property = 42; // now datum2 property is 42, datum1 is unchanged
dat = datum1; // datum2 is unchanged, still has 42
dat.property = 42; // datum1.property is 42

So, what is dat? It is not the object itself, since modifing it changes other objects (would not be the case if it was). It is some way of an indirect access type I mentioned earlier, so it is either pointer or reference. Let's see how it plays. Let's assume it is a reference.

If it would be a reference, though, dat = datum1; // (1) dat = datum2; // (2) would change datum1 (and make it equal to datum2). Since references are aliases, line (1) would establish dat to be an alias to datum1, and line (2) would change an aliased (datum1) to be the same as datum2. Not the case here.

Let's check if pointer logic applies. Let's assume, dat is a pointer. First to lines (dat = datum1; dat = datum2) do fit well - dat is a pointer, and it stopped pointing to datum1 and now points to datum2. So far so good. What about next?

Let's look at dat.property = 42;. If we are to assume dat is a pointer, the only sane way to look at dat. is to assume (.) to be a member dereferencing operator. That is, an operator which dereferences a pointer on the left an access the member of dereferenced object on the right. Read like this, we can clearly see that pointer analogy holds - dereferenced dat points to datum1, and property of datum1 is changed to 42. Same logic applied further on still holds.

So pointer analogy works better than reference! To convince further, let's take into account the fact that objects in JS can be undefined. This makes no sense with references - as they are second names, what might a second name of the no-object mean (no-entity can not have a second or first name), while pointers make everything clear - of course, a pointer, as independent object, can point to nothing.

Granted, those are not the same pointers as in C/C++ - they do not hold a direct memory address, and there is no pointer arythmetics on them. Still, pointer analogy holds much better and clears confusion much easier than a reference one.

Upvotes: 1

Ry-
Ry-

Reputation: 225085

This isn’t a difference between integers and objects*; you’re performing different operations on each. Consider this, no integers involved, but instead the same operation: assigning to a variable, rather than to a property on the object that is the value of that variable:

var a = {};
var b = a;
var c = a;

b.x = 'hello, world!'; // a, b, and c now all refer to { x: 'hello, world!' }

c = { y: 'foo' };      // a and b still refer to { x: 'hello, world!' };
                       // c refers to a different object now

Think of this as everything being a reference, and = overwriting references. You can change what a variable or property references, but changing the property of an object (which a variable or property can reference) changes… the object.

Note that this is the same way as many other common languages work, including C#, Java, and Python.


* Someone might argue primitives are pass-by-value or copied, but as they’re immutable, you can’t tell the difference.

Upvotes: 2

Ramanlfc
Ramanlfc

Reputation: 8354

g is referencing the same object referred by gamelogic, so any changes to object referred by g reflect in gamelogic.

here,

 var b = g.points;
 b = b + 1;

you have set b to literal value 1 and then added 1 to it, so it's now 2 and it doesn't affect g.points .

Upvotes: 0

Related Questions