1

When I associate a structure with a function's prototype and then instantiate multiple objects with that function, they all share the same instance of the structure. I'd like for each object to have it's own instance of the structure without having to explicitly create a new instance when the objects are instantiated; is that possible?

Here's a condensed example of what I'm trying to do:

function defineClass(constructor, instanceMembers) {
    constructor.prototype.model = instanceMembers.model;
    return constructor;
}

var visualObject = defineClass(function () { }, {
    model: {
        setPosition: function (x, y) {
            this.x = x;
            this.y = y;
        }
    }
});

var testA = new visualObject();
var testB = new visualObject();
testA.model.setPosition(10, 10);
console.log(testB.model.x); // outputs "1", but want it to be undefined

That doesn't work, as testA.model and testB.model both reference the same entity.

If I instead clone the structure at the time of construction, then it works, but I'm really hoping to avoid that cloning operation. This code works but hurts me:

function defineClass(instanceMembers) {
    var constructor = function () {
        // Create a copy of the model member in this object instance
        this.model = $.extend(true, {}, this.model);
    };
    constructor.prototype.model = instanceMembers.model;
    return constructor;
}

var visualObject = defineClass({
    model: {
        setPosition: function (x, y) {
            this.x = x;
            this.y = y;
        }
    }
});

var testA = new visualObject();
var testB = new visualObject();
testA.model.setPosition(10, 10);
console.log(testB.model.x); // is undefined, as expected

Does javascript provide a way to do this without the need for the copy-on-instantiation?

Matt Busche
  • 14,216
  • 5
  • 36
  • 61
Jeff Simon
  • 75
  • 6
  • possible duplicate of [Crockford's Prototypal inheritance - Issues with nested objects](http://stackoverflow.com/questions/10131052/crockfords-prototypal-inheritance-issues-with-nested-objects) – Bergi Feb 23 '13 at 17:31
  • It's possible, but I don't believe it's a duplicate as that link does not appear to discuss whether or not it's possible to create 'per-instance' members via prototype (without requiring the code to do an allocation). – Jeff Simon Feb 23 '13 at 18:26
  • *prototype* properties are per definition not unique to the instances that inherit them, if that answers your question. – Bergi Feb 23 '13 at 18:55

2 Answers2

2

I think there is misunderstanding here what prototype is used for. It's used for shared members for each instance of new class. Those are usually function implementations, not values of properties.

Reason why your 2nd example works and reason why Bergie's answer work is that you both first define prototype.model and then you overwrite it later in constructor(?).

In 2nd example in question following happens:

1st constructor.prototype.model = instanceMembers.model;

2nd - after constructor is returned from defineClass, it's executed - this.model = $.extend(true, {}, this.model); and it overwrites model coming from prototype.

So you don't have to put instantiation of property in the prototype, but in the constructor.

Check detailed answer about javascript prototype here.

Community
  • 1
  • 1
Nenad
  • 24,809
  • 11
  • 75
  • 93
  • 1
    This was the answer I was expecting (but not hoping for :)). Namely, that prototype cannot be used in this fashion to define per-instance member variables; instead it's for what I think of as 'static' members. I understand why that is for functions; why would you want duplicate instances of a function? And I understand that in js, functions generally == objects. So it makes sense that objects wouldn't be duplicated. But I was naively hoping :). Moving forward, I'll just have to clone per-instance members (per my approach above) or new them up (per Bergi's approach) in the constructor... – Jeff Simon Feb 23 '13 at 18:31
1

Does javascript provide a way to do this without the need for the copy-on-instantiation?

Yes. Instead of cloning/copying on instantiation, you want your models have a common prototope object:

function defineClass(constructor, prototypeMembers) {
    $.extend(constructor.prototype, prototypeMembers);
    return constructor;
}

var ModelObject = defineClass(function() {}, {
    setPosition: function (x, y) {
        this.x = x;
        this.y = y;
    }
});
var VisualObject = defineClass(function() {
    this.model = new ModelObject;
});

var testA = new VisualObject;
var testB = new VisualObject;
testA.model.setPosition(10, 10);
console.assert(testA.model !== testB.model);
console.log(testB.model.x); // undefined
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I may be mistaken, but I believe this has the same downside as my example #2 - namely, that every time a new VisualObject is created, the constructor has to do an allocation (in my code, via a clone; in your code, via a new). My question is intended to be more about whether or not it's possible to avoid having to explicitly do that allocation. – Jeff Simon Feb 23 '13 at 18:24
  • Um, yes, if you want distinct models for distinct instances you have to create them and allocate memory for them. What's wrong with that? And no, while the constructors are similiar, mine does not create new `setPosition` properties. – Bergi Feb 23 '13 at 18:54
  • 1
    @JeffSimon a clone is considerably more expensive than a single object creation. the latter is precisely what you want: you'll get an empty bag of state for each object, ready to be populated by your methods. – Eevee Feb 27 '13 at 08:50