3

I have a JavaScript array inside a namespace like this:

app.collec.box = [];

and I have a function inside the same namespace like this:

app.init = function () {
    var box = this.collec.box;
    // ... code to modify box
};

I thought that setting a local variable equal to an object or object property was just a REFERENCE to the original, but it seems I am wrong, after changing the contents of the local box variable inside my function, app.collec.box does not change.

Please help, what am I doing wrong? how can I solve this?

Thanks in advance.

EDIT. This is the complete code.

var app = {
    collec: {
        box: [],
        cache: []
    },

    init: function () {
        var box = this.collec.box;

        $.ajax({
            url: 'file.json',
            success: function (json) {
                // Map JSON array to box array using Underscore.js _()map
                box = _(json).map(function (o) {
                    return new Model(o);
                });
            }
        });
    }
};

app.init();
VerizonW
  • 1,785
  • 5
  • 21
  • 27

6 Answers6

6

References point to objects, not variables. box is not a reference to the variable this.collec.box; rather, box and this.collec.box are references to one specific object in memory. You can modify the properties of this object through either of these variables, but you can't use one variable to modify another variable.

If you want to modify what this.collec.box refers to, you either need to set it directly like this:

this.collec.box = ...;

or use a reference to the this.collec object and modify its box property:

var x = this.collec;
x.box = ...;

Edit: Maybe a couple of diagrams will make it easier to understand what's happening.

When you assign box = this.collec.box, this is what actually happens:

this.collec.box -----> (object) <----- box

Both the variables point to the same object in memory, but in no way does box actually refer to the this.collec.box variable.

What you are expecting would work if this happened:

box -----> this.collec.box -----> (object)

but this doesn't happen.

casablanca
  • 69,683
  • 7
  • 133
  • 150
  • This answer contains incorrect information. The code posted above will work as the OP expects, *if* app.init is called in a way such that `this` actually refers to `app.init` (for example, simply calling `app.init()`. – Jason LeBrun Feb 03 '11 at 03:39
  • @Jason: What are you talking about? Clearly this answer anticipates `this` referring to `app`. OP hasn't shown *what* he's doing with `box`. – user113716 Feb 03 '11 at 03:43
  • @Jason LeBrun: As I understand the question, the OP is trying to do `box = ...` inside the function and is asking why this change doesn't reflect in `this.collec.box`. – casablanca Feb 03 '11 at 03:43
  • No, `this` absolutely does not have to refer to app, depending on the context in which `app.init` is called. Arrays variables are indeed references, `var anarray = someOtherArray` does not create a copy of `someOtherArray,` it creates another variable which will modify `someOtherArray` if it's used. Run this in a JS console and you'll see that it works: `var o = {}; o.box =[]; o.init = function() { var l = this.box; l[0] = 'foo'; }; o.init(); console.log(o.box[0]);` – Jason LeBrun Feb 03 '11 at 03:49
  • @Jason LeBrun: I think you're missing the point of this answer. Of course `l[0] = 'foo'` will work, but will `l = 10` change the value of `o.box`? No, and that's what I said. – casablanca Feb 03 '11 at 03:50
  • 1
    @Jason: As I said, this answer *anticipates* `this` referring to `app`. As such, the information in this answer is correct. Just because you're assuming that `this` is not referring to `app` doesn't make this answer wrong. There simply isn't enough info in the question to determine what the problem is. – user113716 Feb 03 '11 at 03:53
  • I see what you're saying. I made the assumption that "code to modify box" implied using array mutator functions, not actually assigning to box again. I guess more code in the original question would clear this up right away. – Jason LeBrun Feb 03 '11 at 03:53
  • On the bright side, I think we've collectively covered about 90% of the potential issues he could be having ;-) – Jason LeBrun Feb 03 '11 at 04:01
  • Just saw his edit, and you have indeed nailed his problem. If you make any edit to your answer, I can upvote it. :-) – Jason LeBrun Feb 03 '11 at 04:04
  • Thanks! this works, however I don't understand why box and this.collec.box are both references – VerizonW Feb 03 '11 at 04:57
  • @VerizonW: I've updated the answer and I hope it's somewhat easier to understand now. – casablanca Feb 03 '11 at 16:02
  • @Jason: I just edited the answer, so you can remove the downvote if you want. :) – casablanca Feb 03 '11 at 16:02
3

What you are doing should work.

I think your problem is with the keyword this (although I can't be certain without seeing more of your code). this is likely referring to the function (app.init) itself, not app.

To troubleshoot try changing ...

var box = this.collec.box;

... to ...

var box = app.collec.box;

[EDIT]

After seeing more of your code, my answer still stands: just replace this with app.

You should also put that line (var box = app.collec.box;) inside the callback function. I don't think the callback will keep the reference to box as it is an async call.

RunnerRick
  • 949
  • 2
  • 12
  • 21
1

since javascript is lexically scoped, the this keyword will be referencing the anonymous function instead of the actual app keyword, like what @rick roth is saying. you should make a closure and have something like this outside of your anonymous function:

var ns = this;

and then you would do

ns.collec.box = [];

ns can be whatever you want, but then within app.init, you would reference the array like so:

app.init = function () {
    var box = ns.collec.box;
    // ... code to modify box
};

so then, as long as your closure is set up correctly, anything pointing to ns, will be referenced correctly.

hellatan
  • 3,517
  • 2
  • 29
  • 37
0

You can try using

app.init = function (box) {
    // ... code to modify box
};

app.init(this.collec.box);
JK.
  • 21,477
  • 35
  • 135
  • 214
0

You're probably passing app.init in such a way that the context of app is being lost. When you do something like div.onclick = app.init, or setTimeout(app.init, 1000), when app.init finally gets called back, this will not point to app, it will point to div or window respectively.

If you need to assign an object's method as a callback, use a closure. For example:

div.onclick = function() { app.init() };

Jason LeBrun
  • 13,037
  • 3
  • 46
  • 42
0

Just a hunch:

You could be using one of the Array instance methods that return a new array instance instead of modifying the original array. Are you by any chance using either concat, join or slice? These are accessors, not mutators.

Also:

If you're modifying the box local variable, make sure you assign it back when you're done:

var box = this.collec.box;
// ... code to modify box
this.collec.box = box;
Ates Goral
  • 137,716
  • 26
  • 137
  • 190