1

I am observing a (for me) not explainable behaviour in my App. I have a few includes via require(). In one of them, called "addUserFunction.js" I have two functions and an array containing objects.
This is the definition for the objects-array:

exports.itemList = [{itemType:"info",itemName:"Information"},{...},{...}];

This is the definition for the two functions:

exports.getItem = function(data) {
    var returnItem = {};
    for (var i=0;i<exports.itemList.length;i++) {
        if (exports.itemList[i].itemType == data.itemType){
            returnItem = exports.itemList[i]; // This line seems to pass a reference, not assign/copy
        }
    }
    returnItem.itemName = (data.itemName) ? data.itemName : returnItem.itemName;
    return returnItem;
}

exports.createPlan = function(formValues) {
    var returnItem = {
        id: 0,
        description: 'foobar',
        items: [
            exports.getItem({itemType:'info',itemName:'Information'}),
            exports.getItem({itemType:'somethingelse'}),
            exports.getItem({itemType:'bla',itemName:'yougottheidea'})
        ]
    };
    return returnItem;
}

Now the problem is: as soon as I use the function exports.getItem and overwrite some of the properties, getItem seems to link the newly created returnItem with the original itemList[i]. So the next call of itemList[i] uses the property values of the first call. Hope that was explained understandable. How can this be?

createPlan() is called from another required() file like this:

var aF = require('addUserFunctions');
function addUser() {
    var formValues = {name: 'test'};
    formValues.foo = aF.createPlan(formValues);
}
module.exports = addUser;
Zim84
  • 3,404
  • 2
  • 35
  • 40
  • 1
    "Seems" to link? It *does* link. You want to copy your objects, not get their references. How shallow/deep a copy you need, I'm not sure. Probably a deep copy. There's several ways to do this. – JayC Mar 29 '13 at 23:23
  • Oh well, what a stupid mistake. Thank you alot! If you convert your comment into an answer you will get an upvote as well as an accepted. :) – Zim84 Mar 30 '13 at 10:21

2 Answers2

1

Whenever you assign a variable or a property an object (this includes all kinds of objects, even arrays), what gets assigned is a value of the reference to the object instance. That is, if I have

a = {};
b = a;

then a.foo = 1, then also b.foo === 1. But then if you do a = null;, b still points to the same object it was pointing to before. All you were doing in your code was digging through your array, finding an appropriate entry, an returning a reference to that entry. What you meant to do was to use that reference as a template for creating a new object. There are several ways to create such an object. The most common way is to use one of a common library's extend methods. However, if your object is more than one level deep, or your object is composed of thing other than trivial objects, things might get tricky, and it might not be possible using these generic methods to create a deep copy of your objects, only shallow ones.

The simplest approach is modify your getItem to clone your templates specifically copying into the new objects the properties you need. If any property in your template references some object that you may need a copy of, be sure to make a copy of that object as well.

JayC
  • 7,053
  • 2
  • 25
  • 41
0

The reason is that I do not copy the element, I only link to it since it's an object I want to copy. = does not copy objects, only simple variable types such as arrays, integers, booleans and so forth.

I created a new function that knows the structure of this object and copies property by property:

exports.copyListItem = function(item) {
    var newItem = {};
    newItem.itemType = item.itemType;
    newItem.itemName = item.itemName;
    // and so on
    return newItem;
}

Not the most elegant way but it works. For a more general approach, have a look here: Most elegant way to clone a JavaScript Object

I'm answering my own question since the original answer is only a comment and does not react (so far) to my counter-comment.

Community
  • 1
  • 1
Zim84
  • 3,404
  • 2
  • 35
  • 40
  • 1
    BTW, Arrays are just like other objects. `a = {stuff:[]}; b = a; b.stuff.push("frist!");` means the array `a.stuff` points to (which is the same array) now has an element "frist!". – JayC Mar 30 '13 at 15:27
  • Better example: `a=[];b=a;b.push("something");`... `a` and `b` still point to the same array, which now has a single element. – JayC Mar 30 '13 at 15:29