1

I have a function, with this inside it:

var fobj = function(){};

var my_obj = fobj.extend({
    test: function(){
        this.year = 1999;  
        my_fmagic.doMagic([
            {
                field: 'year', 
                type: 'function', 
                value: function(data_from_doMagic){

                    console.log(data_from_doMagic, this.year);

                    return data_from_doMagic == this.year;
                }
            }
        ]);
    }
});

Then, the doMagic function:

var fmagic = function(){};

var my_fmagic = fmagic.extend({
    doMagic: function( rules ) {
        var data_from_doMagic = 1999;
        _.each(rules, function(rule) { 
            switch(rule.type) {
                case "function":
                    var f = _.wrap(rule.value, function(func){
                        return func( data_from_doMagic );
                    });
                    f();
                    break;
            }
        });
    }
});

The idea is: pass an array of "rules" to doMagic and let it do the rest.

Expected result: test() should return true (well, in this example it won't return anything, obviously, because there's no return, but the value field of the rule should be true.)

Problem: Instead the result being true it's false, because the function f() in doMagic doesn't know what the variable year is, and so it compares "1999" with "undefined".

As you can see I know where the problem is, but I can't think about any solution.

Ideas?

Regards

7
  • I don't know underscore.js, but in your debugger place a breakpoint on the line return data_from_doMagic == year;. Does the script ever reach that point? And if it does, what's the value of year? Commented May 30, 2012 at 10:57
  • The code presented works correctly if you call f: jsbin.com/exaqad There's no scope issue. The function you're passing as value clearly has access to year because it closes over the context in which year is defined, and similarly the wrapper function has access to data_from_doMagic for the same reason. Commented May 30, 2012 at 11:09
  • No, sorry for not being clear. This a simple example, and test() lives inside a BackboneJS View. The doMagic lives inside another library. @T.J. Crowder f() get's called inside doMagic(), after declaring that variable. (I had a typo in the question). Here's an example: jsfiddle.net/4J2E8 Commented May 30, 2012 at 11:11
  • @alexandernst: Your fiddle is just a copy of the above, without even the f() you've now added to the question, and throws an Unexpected identifier error (probably because you didn't include backbone). The idea is to demonstrate the problem. Commented May 30, 2012 at 11:13
  • 2
    Also, not relevant to the problem, but IMO explicitly relying on type conversion in the equality is a little sketchy--pick a data type. Commented May 30, 2012 at 11:16

1 Answer 1

1

The code you posted to your (original) question was fundamentally different from the code you ended up showing in your fiddle demonstrating the problem.

The problem is here:

test: function(){
    this.year = 1999;  // <=== This isn't a variable, as you originally showed
    my_fmagic.doMagic([
        {
            field: 'year', 
            type: 'function', 
            value: function(data_from_doMagic){

                // *** `this` here won't be the same as `this` was above ***

                console.log(data_from_doMagic, this.year);

                return data_from_doMagic == this.year;
            }
        }
    ]);
}

The quick fix is:

test: function(){
    var self = this;   // <=== Create a variable to close over

    this.year = 1999;

    my_fmagic.doMagic([
        {
            field: 'year', 
            type: 'function', 
            value: function(data_from_doMagic){

                console.log(data_from_doMagic, self.year); // <== And use it here

                return data_from_doMagic == self.year;     // <== And use it here
            }
        }
    ]);
}

Updated fiddle

This happens because in JavaScript, this is determined entirely by how a function is called, not where it's defined. When calling the function you're passing as value, this will be the global object (window, on browsers) because the code is doing nothing to tell the engine what this should be.

So to solve it, the easy answer is to create a variable in the context of the call to the test function and assign it the value of this; then we use that variable (self) rather than this within the value function, because the value function closes over the context of the call to test.

More to explore (disclosure: these are all from my blog):

Sign up to request clarification or add additional context in comments.

2 Comments

Ah, that one was a really silly one :( Thank you, it works correctly!
@alexandernst: We've all done something like it in our time. :-) Glad that helped,

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.