1

I'm creating a bunch of objects and I want to use some of the objects in others, so I'm passing them in so I can update them later. I'd expect to hold a reference to each object, but if I pass an object before it's created, it stays undefined even after I create it. Here's the code:

this.screen = new BatchScreen(screenList);
this.fieldData = new FieldData(this.screen, this.nutrientData);
this.nutrientData = new NutrientData(this.screen, this.fieldData);

function FieldData(screen, nutrientData)
{
    this.nutrientData = nutrientData;
}

this.fieldData.nutrientData is "undefined", which I don't understand as I thought this.fieldData holds a reference to this.nutrientData, which I create after it's assigned in this.fieldData.

christian.stock
  • 181
  • 2
  • 15
  • possible duplicate of [this inside function](http://stackoverflow.com/questions/1963357/this-inside-function) – PM 77-1 Jan 13 '15 at 23:09
  • 3
    How do you "*pass an object before it's created*"? – RobG Jan 13 '15 at 23:15
  • @RobG - obviously you just use jQuery and delegate the dropping to a parent object. – adeneo Jan 13 '15 at 23:18
  • 1
    @adeneo—ah, must be part of the new crystal ball plugin. – RobG Jan 13 '15 at 23:21
  • @RobG - I was being lazy, I could set this.nutrientData = null first, which would work in any other language, but to Javascript it's the same in this context. – christian.stock Jan 13 '15 at 23:24
  • Here is a simpler version of your problem: `var foo = 42; bar = foo; foo = 21;`. You expected `bar` to be 21, but that's not how JavaScript works. The type of value doesn't matter. – Felix Kling Jan 13 '15 at 23:28
  • @Felix Kling - sorry, no. If I set this.screen = null, this.fieldData.screen will also be null. This is a different problem which is well explained by the answer I accepted. – christian.stock Jan 14 '15 at 00:01
  • I reduced the problem to the simplest case that demonstrates that JS is not *pass-by-reference*. If you like it a bit more specific to your case: `var nutrientData = this.nutrientData; this.fieldData = {nutrientData: nutrientData}; this.nutrientData = 42;`. You expected that setting `this.nutationData` will also change `nutationData` and through that `this.fieldData.nutationData`. I just removed the layers of indirection to make the problem easier to see. – Felix Kling Jan 14 '15 at 00:30

3 Answers3

4

Short: you are not passing references.

Long: the evaluation strategy in JS is something called Call by sharing. It also is explained as "references passed by values".

What it means in details is that - in your

this.fieldData = new FieldData(this.screen, this.nutrientData);

statement the this.screen holds a reference, so it's passed by value. And the this.nutrientData holds nothing - so undefined is passed.

Then when you run

this.nutrientData = new NutrientData(this.screen, this.fieldData);

statement the this.nutrientData is being defined as a reference to a new object. But it's too late - we have already passed the undefined in the FieldData constructor.

So the best way to understand it - is to always think that the data is always passed by values, but sometimes those values keep references.

For your code to work as you expect the self.nutrientData and others must be pointers, which JS does not support in any way.

The solution:

if you have 2 mutially dependent objects then you cannot have both in their own constructors. One of them just have to expose additional setter and accept the dependency through it.

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • Thanks - that explains why it doesn't work. Still looking for a fix, is it even possible or do I have to pass "this" (the parent), which I'm trying to avoid. – christian.stock Jan 13 '15 at 23:26
2

Not an answer but a possible solution. You really need a getter but support isn't sufficiently widespread. If you have interdependent properties, you can implement a getter for one (or perhaps both for consistency) so you have:

this.screen = new BatchScreen(screenList);

// Don't bother passing this.nutrientData since you know it's not defined yet,
// pass in the object that you want the nutrientData property of
this.fieldData = new FieldData(this.screen, this);
this.nutrientData = new NutrientData(this.screen, this.fieldData);

// In here, use a function to return the property so its read when called, 
// not when defined
function FieldData(screen, obj) {
    this.getNutrientData = function () {return obj.nutrientData};
}

Then instead of:

var foo = someInstance.fieldData.nutrientData;

use:

var foo = someInstance.fieldData.getNutrientData();

But there seems to be a circular relationship between fieldData and nutrientData.

RobG
  • 142,382
  • 31
  • 172
  • 209
0

In this case, this.nutrientData in the second line, is not being passed as a reference, since no reference to an object exists (at that point there is no this.nutrientData, so you are literally passing undefined).

I'd guess you are also having scope issues with this;

Try this (no pun intended):

var self = this;
this.screen = new BatchScreen(screenList);
this.fieldData = new FieldData(self.screen, new NutrientData(self.screen, self.fieldData));
// assumes that the nutrientData property exists from the object passed into FieldData
this.nutrientData = this.fieldData.nutrientData;
function FieldData(screen, nutrientData)
{
    this.nutrientData = nutrientData;
}
jookyone
  • 141
  • 7