8
votes

I have a simple JSFiddle here demonstrating my issue.

I have this JavaScript code:

var b = document.getElementById("b");

function A() {
    this.f = "1";
}

A.prototype.t = function() {
    b.innerHTML = this.f;
};

var a = new A();

var l = a.t;
l();

Why is this undefined when I try to call a.t? How do I recover that context without being overly verbose or storing too much?

3

3 Answers

15
votes

Why is this undefined when I try to call a.t?

Because in JavaScript, this is set primarily by how the function is called, not where it's defined. a.t() sets this to a within the call, but l() sets this either to undefined (in strict mode) or the global object (in loose mode).

More (on my blog):

The only exceptions are "bound" functions (as with Function#bind) or ES6's "arrow" functions (which get their this from the context in which they're defined).

How do I recover that context without being overly verbose or storing too much?

Function#bind is usually a good answer:

var l = a.t.bind(a);
l();

It returns a new function that, when called, calls the original with this set to the first argument you gave bind. (You can also bind other arguments.) It's an ES5 function, but if you need to support really old browsers, you can easily polyfill it.


If you just need to call l with a specific this value, and not always have it use that value, as Robert Rossmann points out you can use Function#call or Function#apply:

l.call(this, 'a', 'b', 'c');    // Calls `l` with `this` set to `a` and args 'a', 'b', and 'c'
l.apply(this, ['a', 'b', 'c']); // Calls `l` with `this` set to `a` and args 'a', 'b', and 'c' -- note they're specified in an array
1
votes

JavaScript is functionally scoped,

To execute a function with the right value for this, you will need to bind it to the right object. For example,

var l= a.t.bind(a);
1
votes

Because the context changes when you assign the function to a new variable. You could always just do a.t();.