This is my example code:
var foo = {a : 1};
var bar = foo;
console.log(bar.a);//1, as expected
foo.a = 2;
console.log(bar.a);//2, as expected, as objects are passed by reference
foo = {a : 10};
console.log(bar.a);//2, not expected, I expected 10
The last log doesn't give the expected result.
Thinking that foo = {a : value} is the same as foo.a = value I expected that last result was 10.
What's wrong with my expectation? I think I am missing a big lesson here.
You set foo initially to one object:
var foo = {a : 1};
and later overwrite it with completely new object:
foo = {a : 10};
In this case foo and bar are no longer connected, because the reference is now broken.
When you reach this line of code:
foo = {a : 10};
foo is assigned a new variable; from this point on foo and bar are two different variables and for this reason the last line prints 2 as bar is still pointing to the old value.
You are reassigning foo to a new object literal on the 2nd to last line.
Bar still points to the old object, thus you get 2 (which you modified on the 4th line).
If that explanation doesn't clear things up, it might be helpful to step back and try to understand variables, objects, and references on a higher level. Eloquent JavaScript's Data Structures chapter might be a good place to start.
edit: A point worth clarifying: it's important to understand you're not overriding anything, you just changed foo's reference. They point at different things in memory now. That's why bar is the same.
var foo = {a : 1}; //foo refering to object {a : 1}
var bar = foo; //bar refering to same object as foo
console.log(bar.a); //1, as expected
foo.a = 2; //The object to which bar and foo are pointing gets changed
console.log(bar.a); //2, as expected, as objects are passed by reference
foo = {a : 10}; //foo starts pointing to the newly created object, whereas bar is still refering the old object(no change in bar) and the earlier object `{a : 1}` exists and poinyted to by bar
console.log(bar.a); //2, not expected, I expected 10 //hence this result
first you have assigned foo to bar at that time the ref of foo is sent to bar.
now if you change the value bar.a then foo will be changed
foo = {
a: 2
}
bar = foo
console.log(bar.a) //gives 2
bar.a = 30
console.log(foo.a) //gives 30
but when you over-write foo, the connection will be lost and both act like 2 individual variables
so now
foo.a = 20
console.log(bar.a) //gives 30
console.log(foo.a) //gives 20
Related
var foo = {n: 1};
foo.x = foo = {a: 2};
console.log(foo.x);
// Output -- undefined
var a = 1;
c = b = a;
console.log(a,b,c);
// Output -- 1, 1, 1
Could you please explain why first example returns undefined where as second returns 1,1,1?
That happens because expressions in javascript are evaluated left to right.
So in this expression:
foo.x=foo={a:2}
first the foo is dereferenced and a property x is set to be the target of the right operand foo={a:2} result.
Then, while the right operand is evaluated, the foo value is reassigned, so a reference to the previous instance of the object is lost.
To demonstrate it we may simply create another variable to keep it:
var foo = {n:1};
var bar = foo;
foo.x=foo={a:2};
console.log(foo, bar);
That's because the {a:2} object is actually being assigned to the {n:1}.x property! Not to {a:2}. There is no "x" property in it.
What we have here is:
var foo = {n:1};
foo.x = foo = {a:2}; // equals:...
{n:1}.x = {a:2};
/* note that foo.x is actually referring to {n:1} object! */
foo = {a:2}; // ==>
/* while foo is being pointed to a different object
which is now the {a:2} object!
wherefore ...*/
{a:2}.x // is normally undefined i.e.: never set!
However:
{n:1}.x // has been successfully assigned with:
>> {a:2} object.
But because you are loosing a reference to the object {n:1}, and have no other way to examine it. In order to verify and prove that it is {n:1} who is actually receiving the property "x", we'll have to give it a backup reference before we overwrite the foo variable in order to be able to return it.
var foo = {n:1};
var bak = foo;
foo.x=foo={a:2};
console.log(foo.x);
console.log(bak.x);
>> undefined
>> {a:2}.
foo.x = foo = {a: 2};
determines that foo.x refers to a property x of the {n: 1} object, assigns {n: 2} to foo, and assigns the new value of foo – {n: 2} – to the property x of the {n: 1} object.
The important thing is that the foo that foo.x refers to is determined before foo changes.
The assignment operator associates right to left, so you get:
foo.x = (foo = {n: 2})
The left hand side is evaluated before the right hand side and hence an undefined value
This question already has answers here:
JavaScript code trick: What's the value of foo.x
(6 answers)
Closed 6 years ago.
The assignment operator has right-to-left associativity. So
var x,y;
x=y=1;
works as expected and x equals 1. Consider now the code:
var foo={};
foo.x = foo = {n: 2};
I would expect the above to work like the following:
var foo = {n: 2};
foo.x=foo;
However, in the first case foo.x is undefined while in the second case foo.x points to foo (circular reference). Any explanation?
JavaScript evaluates expressions from left to right. We can show what's going on by using an additional variable:
var foo = {};
var bar = foo; // keep a reference to the object originally stored in foo
foo.x = foo = {n: 2};
Because of associativity, the last statement is parsed as:
foo.x = (foo = {n: 2});
But because of evaluation order, foo.x runs first (determining where to store the value), then foo, then {n: 2}. So we store {n: 2} in the variable foo, then assign the same value to the property x of the old contents of foo ... which we can see by looking at bar:
foo = {"n" : 2}
bar = {"x" : {"n" : 2 }}
I understand that the assignment operator is right associative.
So for example x = y = z = 2 is equivalent to (x = (y = (z = 2)))
That being the case, I tried the following:
foo.x = foo = {a:1}
I expected that the object foo would be created with value {a:1} and then the property x will be created on foo which will just be a reference to the foo object.
(This is actually what happens if I was to separate the multiple assignment statement into two separate statements foo = {a:1};foo.x = foo; )
The outcome was actually:
ReferenceError: foo is not defined(…)
So then I tried the following:
var foo = {};
foo.x = foo = {a:1};
Now I don't get the exception anymore but foo.x is undefined!
Why is the assignment not working as I expected?
Disclaimer: The 'duplicate' question seems to be very different to the one that I'm asking, as the issue there is that the variables that were created in the assignment were global, as apposed to variables created with the var keyword. That's not the issue here.
There's an important difference between associativity and order of evaluation.
In JavaScript, even though the assignment operator groups right to left, the operands are evaluated left to right before the actual assignments are performed (which do occur right to left). Consider this example:
var a = {};
var b = {};
var c = a;
c.x = (function() { c = b; return 1; })();
The variable c initially references a, but the right-hand side of the assignment sets c to b. Which property gets assigned, a.x or b.x? The answer is a.x because the left-hand side is evaluated first, when c still references a.
In general, the expression x = y is evaluated as follows:
Evaluate x and remember the result.
Evaluate y and remember the result.
Assign the result from step 2 to the result of step 1 (and return the former as the result of the expression x = y).
What happens with multiple assignments, as in x = (y = z)? Recurse!
Evaluate x and remember the result.
Evaluate y = z and remember the result. To do this:
Evaluate y and remember the result.
Evaluate z and remember the result.
Assign the result from step 2.2 to the result of step 2.1 (and return the former as the result of the expression y = z).
Assign the result from step 2 to the result of step 1 (and return the former as the result of the expression x = (y = z)).
Now let's look at your example, slightly edited:
var foo = {};
var bar = foo; // save a reference to foo
foo.x = (foo = {a:1}); // add parentheses for clarity
foo.x is evaluated before foo gets assigned to {a:1}, so the x property gets added to the original {} object (which you can verify by examining bar).
Edited the answer to make it simple
First of all you have to understand the differnce between Reference- and Value- Type.
var foo = {};
foo variable holds a Reference to an object in memory, lets say A
Now, there are two arts of accessors: Variable Accessor and Property Accessor.
So foo.x = foo = {a:1} can be understood as
[foo_VARIABLE_ACCESSOR][x_PROPERTY_ACCESSOR] = [foo_VARIABLE_ACCESSOR] = {a:1}
!!! Accessor chain is evaluated first to get the last accessor, which is then evaluated associative.
A['x'] = foo = {a:1}
Property Accessor are seperated into setters and getters
var foo = { bar: {} };
foo.bar.x = foo = {a:1}
Here where have decared two nested objects foo and bar. In memory we have then two object A and B.
[foo_VAR_ACCESSOR][bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> A[bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B[x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B['x'] = foo = {a: 1}
Here you have little example
var A = {};
var B = {}
Object.defineProperty(A, 'bar', {
get () {
console.log('A.bar::getter')
return B;
}
})
Object.defineProperty(B, 'x', {
set () {
console.log('B.x::getter')
}
});
var foo = A;
foo.bar.x = foo = (console.log('test'), 'hello');
// > A.bar.getter
// > test
// > B.x.setter
Great question. The thing to remember here is that JavaScript uses pointers for everything. It's easy to forget this since it is impossible to access the values representing memory addresses in JavaScript (see this SO question). But realizing this is very important in order to understand many things in JavaScript.
So the statement
var foo = {};
creates an object in memory and assigns a pointer to that object to foo. Now when this statement runs:
foo.x = foo = {a: 1};
the property x is actually getting added to the original object in memory, while foo is getting assigned a pointer to a new object, {a: 1}. For example,
var foo, bar = foo = {};
foo.x = foo = {a: 1};
shows that if foo and bar are pointing to the same object initially, bar (which will still point to that original object) will look like {x: {a: 1}}, while foo is simply {a: 1}.
So why doesn't foo look like {a: 1, x: foo}?
While you are right in that assignments are right associative, you must also realize that the interpreter still reads from left to right. Let's take an in-depth example (with some bits abstracted out):
var foo = {};
Okay, create an object in memory location 47328 (or whatever), assign foo to a pointer that points to 47328.
foo.x = ....
Okay, grab the object that foo currently points to at memory location 47328, add a property x to it, and get ready to assign x to the memory location of whatever's coming next.
foo = ....
Okay, grab the pointer foo and get ready to assign it to the memory location of whatever's coming next.
{a: 1};
Okay, create a new object in memory at location 47452. Now go back up the chain: Assign foo to point to memory location 47452. Assign property x of the object at memory location 47328 to also point to what foo now points to--memory location 47452.
In short, there is no shorthand way to do
var foo = {a: 1};
foo.x = foo;
Consider the following statements:
var foo = {n: 1};
foo.bar = foo = {n: 2};
Can you explain why foo.bar is undefined instead of being foo?
When executing the assignment operator, JS evaluates the left part first. So this
foo.bar = foo = {n: 2};
is interpreted as
evaluate foo.bar. This returns a reference {base: Object {n:1}, property:bar}.
then evaluate the second assignment:
2.1 eval foo. This returns a reference {base:<scope>, property:foo}
2.2. eval {n:2}. This creates a new object.
2.3 put value: <scope>.foo = {n:2}
2.4 return {n:2}
put value to the first reference: {n:1}.bar = {n:2}. This runs fine, but the old object {n:1} is not accessible anymore, since <scope>.foo already refers to the new object
Details: http://ecma-international.org/ecma-262/5.1/#sec-11.13.1
If you make a copy of foo before, you'll see that the leftmost = actually modifies the old object:
var foo = {n:1};
var oldFoo = foo;
foo.bar = foo = {n: 2};
document.write(JSON.stringify(foo) + "<br>")
document.write(JSON.stringify(oldFoo) + "<br>")
So by the time the assignment for foo.bar happens, the reference is "filled in" for foo. which makes it the original object.
Let's expand your code a bit to make it more clear.
var foo1, foo2;
foo1 = foo2 = {n:1};
foo1 === foo2; // true
foo1.bar = foo1 = {n:2}
foo1.bar === foo2; // false
foo1 === foo2; // false
There are two objects at play here. One will have a bar property, the other wont. To show this, i'll store the original object in another variable for comparison.
var foo = {n: 1};
var orig = foo;
foo.bar = foo = {n: 2};
console.log(foo, orig); // {n:2}, {n:1, bar: {n:2}}
Until the foo.bar line is done executing, foo still contains the original object, so the bar property of the original object will be set to the new object.
You changed the object to which foo refers. There is no bar in {n: 2}. What did you expect?
Why is that this code:
var foo = {one: 1, two: 2};
var bar = new Object( foo );
bar.three = 3;
bar.one = 100;
document.write(bar.one); //100
document.write(foo.one); //100
results in bar.one & foo.one being both 100, while
var foo = {one: 1, two: 2};
var bar = Object.create( foo );
bar.three = 3;
bar.one = 100;
document.write(bar.one); //100
document.write(foo.one); //1
only affects bar.one..
My first intuition is that since in the first piece of code we are assigning a foo reference to bar, then it means the change will also apply to foo, while on the second code, it probably 'inherits' from foo, and therefore the change on bar's 'subclass' attribute won;t apply to its 'superclass' (prototype)..
Can somebody please confirm that my assumption is at least on the right track? Would absolutely appreciate any answers. Thanks in advance.
The line:
var bar = new Object( foo );
In your first snippet, it doesn't do anything -you are right with your assumption-, it will simply return a reference to the same object passed to the Object constructor.
That's the behavior when you pass a native object to the Object constructor in a new expression (new Object(value)), if you pass a host object, the results are implementation dependent.
If you don't pass a value (or you explicitly pass the primitives undefined or null) a new object that inherits from Object.prototype will be created.
Otherwise, if you pass any of the remaining primitives (as a Number, String or a Boolean value), a primitive wrapper object will be created (basically "primitive-to-object" type conversion), for example.
var s = new String("foo"); // a string object wrapper
typeof s; // "object"
s.valueOf(); // "foo"
See this question about primitives and objects: How is a Javascript string not an object?
In your second snippet, the line:
var bar = Object.create( foo );
Creates a new object, that inherits from foo, and since it's a different object, when you assign the properties:
bar.three = 3;
bar.one = 100;
Those will be created physically on that separated instance, as you can see, the bar.one property shadows the value contained in foo.
The object referenced by bar, in fact will contain two own properties (one and three, but since it inherits from foo, the property named two is resolvable through the prototype chain, for example:
bar.hasOwnProperty('one'); // true, the property is "own"
bar.hasOwnProperty('two'); // false, the property is "inherited" from foo
bar.two; // 2, is accessible
Basically, the prototype chain of bar looks like this:
-----------------
========> | Object.prototype| ==> null
| -----------------
|-------------| [[Prototype]] |---------|
| one: 100 | ====================> | one: 1 | (shadowed)
| three: 3 | | two: 2 |
|-------------| |---------|
(== line denotes the prototype chain)
new Object and Object.create do completely different things, despite their similar sounding names.
new Object(), with no argument, is equivalent to an empty object literal {}, which you should always use instead. With an argument, new Object(obj) simply returns the argument.
Object.create(obj) helps untangle JavaScript's messed up prototypal inheritance. Reading this article by Douglas Crockford will help with that. Basically, it creates a new object that inherits from obj. So if you have
var obj1 = {x: 1}, obj2 = Object.create(obj1);
obj1.x; // 1
obj2.x; // 1
obj2.x = 42;
obj1.x; // 1
obj1.x = 10;
obj2.x; // 10, since obj2 inherits from obj1, changing obj1's properties
// changes obj2, but not the other way around.