0

I create an object, that I later am trying to use as a prototype. One of the properties of the object is an array. I create two objects using it as a prototype and push some elements into the array of the first one, why do they show up in the array of the second object and how do I fix that, thanks.

Please see the code:

var Category = {

          name: "",
         items: [],
    totalSpent: function(){
      var total = 0;
      this.items.forEach(function(item){
        sum += item.price;
      });
    }

  };

var cat1 = Object.create(Category);
var cat2 = Object.create(Category);

cat1.items.push("item1");

console.log(cat2.items);      //return ["item1"]
Sasha Kolsky
  • 432
  • 1
  • 5
  • 14
  • 1
    You're using the exact same object as the prototype of two different objects; why is the behavior surprising? The `Object.create()` call does not make a **copy** of the object passed into it. – Pointy Sep 25 '14 at 23:36
  • 1
    Prototypical inheritance is about sharing functionality and not data. If you want to copy object contents around that's not `Object.create` (which creates an object with a given prototype) - that's `Object.assign` that copies data over (though that's ES6). – Benjamin Gruenbaum Sep 25 '14 at 23:38
  • Pointy, i was thinking it will work same as an instantiating on object of a class would do. – Sasha Kolsky Sep 25 '14 at 23:45
  • possible duplicate of [How do I assign an object to an array within another object?](http://stackoverflow.com/questions/23864612/how-do-i-assign-an-object-to-an-array-within-another-object) – Andrew Shepherd Sep 25 '14 at 23:46
  • instantiating an object of a class, when the _class_ contains a list, will still have the same behavior. – Eevee Sep 25 '14 at 23:50
  • @Eevee, not if the reference is stored in the instance variable. – Sasha Kolsky Sep 26 '14 at 00:00
  • @Alexander.Kazakov: Right and the same applies here. The array is shared on the prototype, not assigned to a property of the instance. – Felix Kling Sep 26 '14 at 00:03
  • You should read what [*Object.create*](http://ecma-international.org/ecma-262/5.1/#sec-15.9.1) does (try the [*MDN article*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) too). – RobG Sep 26 '14 at 00:58
  • 1
    that's why i said "when the class contains a list", as opposed to instances containing it. – Eevee Sep 26 '14 at 01:05

2 Answers2

1

Object.create does not make a deep copy of the object. It will return an object based on the second argument (or an empty object if second argument not given) with the first argument as it's prototype. If you then mutate members of that prototype it'll affect all instances that use that object as a prototype (in your case Category).

Both cat1 and cat2 have Category as it's prototype; so:

cat1.__proto__ === cat2.__proto__;//true
Category === cat1.__proto__;

To show you an example what you're doing without involving prototype (note: do not use proto, it's a non standard property):

var org = {member:22};
// copy is a reference to org
// same as cat1={};cat1.__proto__ = Category
// or cat1 = Object.create(Category)
var copy = org;
// same as cat1.items.push('item');
// you are mutating Category.items so mutating Category
copy.member=0;//mutating copy, so mutating org as well
console.log(org.member);//=0

With prototype involved things get a little more complicated as re assigning members would actually shadow that member instead of changing the prototype but the above example was for simplicity.

var proto = {member:22,other:{sub:22}};
var copy = Object.create(proto);
//this does not affect proto as it will shadow the member
copy.member=0;
//this will affect proto because you're mutating proto.other
copy.other.sub=0;

In your case the items member is instance specific and should not be defined on the prototype. To initialize instance specific members you can use constructor functions or an init function that initializes the instance before using it.

This, how prototype is used, shadowing and more is explained here.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
-1

Could you just try

var cat1 = new Category();
var cat2 = new Category();

instead of Object.create() to instantiate those objects?

more in depth explanation seen here: http://www.htmlgoodies.com/beyond/javascript/object.create-the-new-way-to-create-objects-in-javascript.html

Eleanor Zimmermann
  • 414
  • 1
  • 8
  • 26