Question

Lets say I have this:

function Foo(){
   this.name = "fooos";
};

Now later in the script I have a reference to Foo, and what to add properties to Foo when instantiated. I could do something like this:

Foo.prototype.myProp = 3;

foo = new Foo();

expect(foo).to.have.property('name');
expect(foo).to.have.property('myProp');

This works great until I need to attach an object to prototype, like so:

Foo.prototype.myProp = { bar : 3 };

The issue is that now instances will share state on bar (as `bar is just a reference):

foo = new Foo();
foo2 = new Foo();

foo.myProp.bar = 5;
expect(foo2.myProp.bar).to.equal(5);

Which is not desirable. However if the object is added inside the constructor the instances do not share state.

Is there a way around this? I don't have access to the constructor to manipulate it. I need to attach an object to an instance from the prototype.

Was it helpful?

Solution

Properties don't have to be on prototypes, you can just add them directly to instances:

var f = new Foo();
f.myProp = { bar: 3 };

I know that. Its not what I want.

The only way I can think of to do this via the prototype with a property (not a method; @meager has a solution using a method) is an ugly hack where you define the property on the prototype with Object.defineProperty (ES5-only) and then redefine it on first access, like this:

Object.defineProperty(Foo.prototype, "myProp", {
    get: function() {
        var obj = { bar: 3 };
        Object.defineProperty(this, "myProp", {
            get: function() {
                return obj;
            },
            set: function(value) {
                obj = value;
            }
        });
        return obj;
    }
});

Live Example | Source

I would not recommend that. :-)


An alternative would be to throw a facade in front of Foo, like this:

var OriginalFoo = Foo;
Foo = function() {
    OriginalFoo.apply(this, arguments);
    this.myProp = { bar: 3 };
};
Foo.prototype = Object.create(OriginalFoo.prototype);

That would, of course, only apply to objects created via new Foo after this code executed.

OTHER TIPS

You cannot do this based on your restriction of needing to go through the prototype. The prototype is shared, you cannot give each instance a unique object through the prototype. It's pretty much impossible by definition of what the prototype is.

The closest thing would be to add an accessor method which lazily adds the property to the specific instance, but this is pretty far removed from your ask:

Foo.prototype.getMyProp = function () {
  this.myProp = this.myProp || { bar: 3; }
  return this.myProp
}

foo = new Foo();
foo2 = new Foo();

foo.getMyProp().bar = 5
expect(foo2.getMyProp().bar).to.equal(3);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top