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:
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).
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?
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:
(* 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:
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.
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. :-)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With