Reputation: 45
var flagArray = [];
var flagTarget = document.getElementById("content");
var flag = {
init: function(country) {
if (country) {
this.country = country;
flagArray.push(Object.create(flag));
}
},
draw: function() {
var pos = flagArray.indexOf(this.country);
if (this.country === "norway") {
flagArray[pos] = '<div class="flag ' + this.country + '"><div class="part1"></div><div class="part2"></div><div class="part3"></div><div class="part4"></div></div>';
} else {
flagArray[pos] = '<div class="flag ' + this.country + '"><div class="part1"></div><div class="part2"></div></div>';
}
flagTarget.innerHTML += flagArray[pos];
},
};
flag.init("ivorycoast");
flag.init("sweden");
flag.init("denmark");
flag.init("finland");
flag.init("norway");
flagArray.forEach(function(flag) {
flag.draw();
});
I've been struggling with this school assignment for too many hours now. Why does the last flag.init value get written 5 times to the array?
Upvotes: 0
Views: 158
Reputation: 1073998
It doesn't, but it seems like it does.
What's happening is that you're updating country
on flag
five times, and each time you're creating a new object that inherits from flag
and pushing that on the array. So the end result is five different objects in the array, all of which inherit from flag
, and none of which have their own country
value. So the country
you see on them is the one inherited from flag
, which is the last value you assigned to this.country
.
If you want them to inherit from flag
but each have their own country
value, you need to remember the object created with Object.create
and set country
on it:
init: function(country) {
if (country) {
var newFlag = Object.create(flag);
flagArray.push(newFlag);
newFlag.country = country;
}
},
(There's a more advanced way I show after the following diagrams.)
Side note: There's a separate problem in draw
, this line:
var pos = flagArray.indexOf(this.country);
should just be
var pos = flagArray.indexOf(this);
Because the array contains objects, not country strings. That said, you don't need to use flagArray
in draw
at all.
Let's throw some ASCII-Art (actually, Unicode-art) at your current code to understand what you're seeing:
You start out with this in memory:
+−−−−−−−−−−−−−−−−−−−−−−−+ flag−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−>| (object) | +−−−−−−−−−−−−−−−−−−−−−−−+ | init: (function) | | draw: (function) | +−−−−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−+ flagArray−−−>| (array) | +−−−−−−−−−−−+ | length: 0 | +−−−−−−−−−−−+
Then after the first init
call, you have:
+−−−−−−−−−−−−−−−−−−−−−−−+ flag−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−>| (object) | | +−−−−−−−−−−−−−−−−−−−−−−−+ | | init: (function) | | | draw: (function) | | | country: "ivorycoast" | | +−−−−−−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−+ | flagArray−−−>| (array) | | +−−−−−−−−−−−+ | | length: 1 | +−−−−−−−−−−−−−−−+ | | 0 |−−−>| (object) | | +−−−−−−−−−−−+ +−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−+ +−−−−−−−−−−−−−−−+
You've created a new object with flag
as its prototype, and set flag
's country
to "ivorycoast"
.
After the second call to init
, you have:
+−−−−−−−−−−−−−−−−−−−−−−−+ flag−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−+−−−−>| (object) | | | +−−−−−−−−−−−−−−−−−−−−−−−+ | | | init: (function) | | | | draw: (function) | | | | country: "sweden" | | | +−−−−−−−−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−−+ | | flagArray−−−>| (array) | | | +−−−−−−−−−−−+ | | | length: 2 | +−−−−−−−−−−−−−−−+ | | | 0 |−−−>| (object) | | | | 1 |−+ +−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−−+ | | [[Prototype]] |−−+ | | +−−−−−−−−−−−−−−−+ | | | | +−−−−−−−−−−−−−−−+ | +−−>| (object) | | +−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−+ +−−−−−−−−−−−−−−−+
...and so on, until flagArray
is filled with five separate objects, all using flag
as their prototype, and flag
has "norway"
for its country
property.
Here's that more advanced way to add country
that I mentioned. If I were writing production code, I wouldn't use it for this (too verbose), but just for completeness: There's a second argument you can give Object.create
that can have a list of properties to add to the new object. You specify the properties by giving their name and an object the specifies the various aspects of the property, like whether it's enumerable, whether it's read-only or writable, its initial value (or a getter and/or setter), etc. The writable
, enumerable
, and configurable
features of a property are all defaulted false
when you do this, but they default true
when you create a property by just assigning to it. So to do the same thing we're doing with newFlag.country = country
, we have to specify all those things and say true
to make the property the same.
So, this does the same thing the version earlier using newFlag
did, just without the temporary variable:
init: function(country) {
if (country) {
flagArray.push(Object.create(flag, {
country: {
enumerable: true,
configurable: true,
writable: true,
value: country
}
}));
}
},
Upvotes: 5
Reputation: 36965
Using Object.create(flag)
you set the flag
as prototype of each element in the array. The prototype is shared between all objects, so this.country
gets updated on the prototype object in each init
call and inherited by every element in the array.
To create a separate object you should try Object.assign
, for example:
flagArray.push(Object.assign(Object.create(flag), { country : country }));
https://jsfiddle.net/p58189f1/
Upvotes: 1