views:

124

answers:

4

Is it possible to extract a functions caller scope?

I have this example:

var myObject = {
    id: 1,
    printId: function() {
        var myId = function() {
            return this.id;
        }
        console.log(myId());
    }
}

myObject.printId(); // prints 'undefined'

The example above prints out undefined, because 'this' seems to be the window scope by default inside new functions (in this case myId). Is there any way I can use a proxy that applies the correct scope before executing?

Something like this (fictive):

var execute = function( fn ) {
    fn.apply(fn.caller.scope);
}

var myObject = {
    id: 1,
    printId: function() {
        var myId = function() {
            return this.id;
        }
        execute( myId );
    }
}
+2  A: 

Try the following:

var myObject = {
    id: 1,
    printId: function() {
        var myId = function() {
            return this.id;
        }
        console.log(myId.apply(this));
    }
}

myObject.printId(); // prints '1'

The problem is that you want to run the function myId inside the scope of printId, not the scope of myId. You can create the function so that it gets the same scope by putting it inside myObject, instead of putting it inside printId:

var myObject = {
    id: 1,
    printId: function() {
        this.myId = function() {
            return this.id;
        }
        console.log(this.myId());
    }
}

myObject.printId(); // prints '1'

Answer to the comment:

var stores the variable in the function scope, which means myId is stored inside printId. This means that this in myId refers to printId, and not myObject. Since printId is stored in myObject, this in printId refers to myObject, and so myId.apply(this) inside printId changes the containing scope of myId from printId to myObject, and therefore this in myId now refers to myObject. In the second example myId is stored in myObject right away, and so this in printId and myId refer to the same object, namely myObject. Does that make any sense at all? :P

Marius
Good answer but you might want to drop the now incorrect comment at the end ;).
Rob Van Dam
This doesn't solve it (maybe I didnt explain good enough). Why would I need apply() when I'm already in the correct scope (myObject)? See my fictive example.
David
I've tried to explain it in my answer. Tell me if that made it any clearer.
Marius
+1  A: 

What's wrong with:

var myObject = {
  id: 1,
  printId: function() {
    return this.id;
  }
}

?

If not that then:

var myObject = {
  id: 1,
  printId: function() {
    var myid = function() {
      return this.id;
    };
    return myid.apply(this);
  }
}

See apply().

cletus
+1  A: 
var myObject = {
    id: 1,
    printId: function() {
        var _this = this;
        var myId = function() { 
            return _this.id;
        }
        console.log(myId());
    }
}
erikkallen
+1  A: 

If you use the Prototype library you could use Function#bind() like this:

var myObject = {
    id: 1,
    printId: function() {
        var myId = function() {
            return this.id;
        }.bind(this);
        console.log(myId());
    }
}

A little overkill perhaps, but if you're using objects like this extensively it might be worth looking up.

brownstone
Best answer so far. The prototype API describes my issue best at http://www.prototypejs.org/api/function/bind
David