0

I use prototypal inheritance and want to have objects with instance arrays. So if I derive some objects from one object with an instance array and access the array, all of them share the array. I want to push something to the array and only change the array in the actual object, not in all others.

What is a elegent solution to this Problem with using standard prototypal inheritance and Object.create?

var sys = require('sys');

var obj ={
    data: [],
    add: function(what){
        this.data.push(what)
    }
};

var one = Object.create(obj);
one.add(1);

var other = Object.create(obj);
other.add(2);

sys.puts(other.data.length); // is 2, but should be 1

4 Answers4

2
var ObjectName = function(){
    this.data = [];
}

ObjectName.prototype.add = function(what){
    this.data.push(what);
};

var one = new ObjectName();
one.add(1);
Ronald
  • 16,033
  • 9
  • 27
  • 29
  • I want to use prototypal inheritence with Object.create – Peterfia Jul 28 '10 at 15:06
  • You can still use object.create. But you have to run the constructor to initialize the object with the instance variable this.data. var one = Object.create(ObjectName.prototype); ObjectName.call(one); one.add(1); – Marco Aug 15 '10 at 00:34
1

There is no elegant solution with Object.create, because You're Doing It Wrong.

What you want is:

function MyArray() {
    this.data = [];  // per-instance data here
}

MyArray.prototype = {
    add: function(what) {  // prototype methods here
        this.data.push(what);
    }
};

var one = new MyArray;
one.add(1);
...
Jason Orendorff
  • 42,793
  • 6
  • 62
  • 96
0

You can also replace:

add: function(what) {  // prototype methods here
    this.data.push(what);
}

with

add: function(what) {  // prototype methods here
    this.data = this.data.concat(what);
}

as this will create a new variable instead of pushing it into it the prototype's instance.

Artefacto
  • 96,375
  • 17
  • 202
  • 225
0

Object.create can add properties to the new object, by passing a second parameter with property descriptors.

var sys = require('sys');

var obj = {
    add: function(what){
        this.data.push(what)
    }
};

var one = Object.create(obj, {
    data: {
        value: [],
        writable: true,
        enumerable: true,
        configurable: true
    }
});
one.add(1);

var other = Object.create(obj, {
    data: {
        value: [],
        writable: true,
        enumerable: true,
        configurable: true
    }
});
other.add(2);

sys.puts(other.data.length); // should be 1

Of course, you would want to put that in a builder function so you don't repeat yourself:

function makeThing() {
    return Object.create(obj, {
        data: {
            value: [],
            writable: true,
            enumerable: true,
            configurable: true
        }
    });
}

Although, at at point you're basically writing a constructor (with the advantage that you don't need to call it with new). Also, if you're setting writable, enumerable, and configurable to true, you could just set the property the normal way, which can be made backward-compatible with ECMAScript 3 by implementing a simple version of Object.create:

function makeThing() {
    var newObj = Object.create(obj);
    newObj.data = [];
    return newObj;
}
Matthew Crumley
  • 101,441
  • 24
  • 103
  • 129