I have object literal within that i have another literal .
The parent instances are setting the values fine, but not the inner one.
I am confused with this behavior.
Why the behavior of object literal is different ?
here is the http://jsfiddle.net/Lq6frf76/
var vehicle={
tires : 0,
seats : {
total :0
}
}
var Car = Object.create(vehicle);
Car.tires=4;
Car.seats.total = 4 ;
var bike = Object.create(vehicle);
bike.tires=2;
bike.seats.total = 2 ;
console.log(Car.tires); --->> it is not overwritten not consistent
console.log(Car.seats.total); -->> it is overwritten here
console.log(bike.tires);
console.log(bike.seats.total);
This is not a duplicate question
What I don't know why the same behavior is not repeated for the parent object
or in other words
why car.tires value is showing correctly instead of overwritten in my example
Edit: Added some more at the end and made a correction
I feel like we need to put down an answer here. I apologize if this is bad etiquette (and if this is the wrong answer). Please let me know.
The way prototypical inheritance works in javascript:
When you read a property of an object, lets say the tires
property of your Car
object, first it checks if Car
has that property itself. What that means is, is there a tires
property directly attached to the Car
object? Or in other words, is Car
something like this:
{
tires: 0,
...
}
And the answer is no. You might think Object.create()
does this, similar to what you might do in a constructor, i.e.
function Vehice(t) {
this.tires = t;
...
};
var Car = new Vehicle(4);
All Object.create()
does is make the object you pass in the prototype
of the object it returns to you.
So after Object.create()
, your object looks something like this
{
__proto__: //reference to vehicle
}
So when it doesn't find tires
directly attached to the object, it then looks at the prototype, in this case a reference to the vehicle
object you created earlier. The prototype (vehicle
) does have the tires
property, so it returns that value.
Correction
So what happens when you say Car.tires = 6
? When you write a value to an object property, things work differently. Javascript only looks to see if the object itself has that property to write to. If the object itself doesn't have that property, it first checks down the prototype chain to make sure the property is not an inherited readonly property. If it isn't, it creates that property on the object itself (so long as it's legal to write that property to the object). So now you have something that looks like this:
{
tires: 6,
__proto__: //reference to vehicle
}
End Correction
Now when you read the tires
property on the Car
object, it first sees the tires
property on the Car
object itself and returns that value, never needing to look at the prototype.
This is why you can set the tires
property on one vehicle and not effect the value of the others.
Now for the seats.total
property. When you read from the seats
property, everything works as before. There is no seats
property on the Car
object itself, so it looks to the prototype, which does have it. Then it checks the object referred to by seats
to see if it has a total
property directly attached to it, and it does, so it just returns that value.
Now when you want to write to the total
property, things are once again different. The statement is this: Car.seats.total = 4
. What happens here is that you are setting the total
property on the object referenced by the seats
property.
Javascript first has to find the object referenced by seats
. It does this by checking to see if it is a property of the Car
object itself. It is not, so it checks the prototype, where it finds the seats
property. Now it can write the total
property to the seats
object as we saw before, but notice that this is happening to the seats
property of the prototype, which is a referene to the vehicle
object defined before and shared by bike
.
In other words, javascript does not see that you are writing to a property of the Car
object, so it doesn't create a seats
property on the Car
object and assign it a new object, and then a total
property on that object and assign it 4
.
Now, when you try to read bike
's seats
property, javascript first looks to see if bike
has a seats
property directly attached to it. It does not, so it looks to bike
's prototype, which does have it and returns the modified vehicle.seats
object.
I hope this clarifies what is happening and answers your questions. Let me know if any part isn't clear, and I hope someone will correct me if I'm just plain wrong!
Addendum: I think coming from a traditional OO language the way it works for tires
is what you would expect to happen: You inherit the initial value, but if you change it, you don't expect it to change for every other instance of vehicle
.
The way javascript does this is very space economical. By only initially having the prototype have the property, if you have a thousand objects inheriting from vehicle
and only 2 of them set the tires
property, you've only used up space for three Number
s instead of a thousand.
As for the way it works with seats
, I suppose they could have designed the language to detect if you were setting a property on an inherited object
(seats
) and then copy the entire object into the instance. My guess is that this would actually make the system less flexible and more complicated. If you wanted Car
to have it's own version of seats
, you could specify it like so:
var car2 = Object.create(vehicle, {
seats: {
value: {
count: vehicle.seats.total
}
}
});
or like so:
var car2 = Object.create(vehicle);
car2.seats = { count: vehicle.seats.total }; //create a new object like the one attached to vehicle
Does that last example make it clearer what's happening?
To me, in this case, an explicit constructor version of this would be more natural, but with javascript we can use constructors or `Object.create(), or various combinations of them to do what we need.
Ultimately though, Every set happens in two parts. First, find the object we are setting the property on; Last, set the property. Everything before that final .
is just there to do the first step:
Car.seats.count = 10
means set the count
property of the object referred to by Car.seats
to 10. What object is Car.seats
? We are now following the rules of reading a property, not writing one. Car
doesn't have a seats
property, so see if it inherits one. If it doesn't, Car.seats
will return undefined
, and trying to set a property on undefined
will throw an error. If it does inherit it, Car.seats
will return the object from somewhere down the prototype chain, in this case the object referred to by vehicle
. This object has a count
property so just set it to 10.
Car.tires = 4
means set the tires
property of the object referred to by Car
to 4. Car
doesn't have a tires
property, so make sure we are allowed to create one and then do so.
I hope this clarifies things. Sorry for writing so much, but doing so has really helped solidify these concepts for me. Plus I learned some ECMAScript 5 stuff that i've been blissfully ignoring :-)