I have this constructor:
var EditableItem = (function() {
function EditableItem(schema, item) {
this.schema = _.clone(schema);
this.item = _.clone(item);
this.propertyTypes = [
'stringProperties',
'booleanProperties'];
}
EditableItem.prototype = {
createItem: function() {
var self = this;
self.propertyTypes.forEach(function(type) {
var properties = self.schema[type];
properties.forEach(function(property, i) {
var itemProperty = self.item[type][property.id];
if (itemProperty) {
properties[i].value = itemProperty;
}
});
});
self.schema.itemId = self.item._id;
return self.schema;
}
};
return EditableItem;
})();
and each time I use it, like this...
async.parallel({
schema: function(callback) {
schemas().findOne({
_id: 'default'
}, callback);
},
items: function(callback) {
items().find().toArray(callback);
}
},
function(err, result) {
if (err) return callback(err);
var all = result.items.map(function(item) {
return new EditableItem(result.schema, item).createItem();
});
callback(null, all);
});
...I end up with an array result where the last item is repeated while the others are omitted.
My guess is that I need to add a closure somewhere, and as you can see I've tried, but still it yields the same result. Any ideas?
EDIT: I've found a solution. It's not pretty, but maybe someone here can explain why it works and offer a better solution...
In the constructor, where I had: this.schema = _.clone(schema);
I changed to: this.schema = JSON.parse(JSON.stringify(schema));
This seems to allocate new memory for the object, while _.clone still keeps some references to the original object (I guess).
Javascript numbers, strings & booleans are equated and passed by value while objects and arrays are equated and passed by reference. As a JSON representation of an object is a string, it will be equated and passed by value.
If working with a JSON representation of schema gives different behaviour from a clone (an object), then we may conclude that the clone still contains references to the same objects as the original, uncloned schema; ie. the clone is not "deep".
As I understand it, underscore.js doesn't yet offer deep clone but Lo_Dash does. You might like to try Lo_Dash's underscore compatibility build.
Alternatively, just stick with your JSON approach, which is doing the same for you as a more formal deep clone method.