8

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
eatonphil
  • 13,115
  • 27
  • 76
  • 133
  • Related: [What does this JavaScript syntax mean? `(0, _parseKey2.default)(something)`](https://stackoverflow.com/q/35522560/4642212). – Sebastian Simon Apr 07 '21 at 05:39

3 Answers3

15

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
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Alternatively, one can use [`Function.prototype.call`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call) if the function does not need to be passed around and only needs to be called once: `var l = a.t; l.call(a);` – Robert Rossmann May 27 '15 at 15:14
  • Concerning the original asker's code, would it be valid if `A.t()` was defined within the original `A` function? This way the function would be able to access a privately-scoped variable ([like this](http://jsfiddle.net/lun471k/re8vdadt/1/)). – Jeff Noel May 27 '15 at 15:15
  • @RobertRossmann could you demonstrate that? – eatonphil May 27 '15 at 15:15
  • @eatonphil: `l.call(a)` will call `l` with `this` set to `a`. – T.J. Crowder May 27 '15 at 15:21
  • 1
    @JeffNoel: Yes. It wouldn't be `this`, but yes. It *might* be "retaining too much," however. – T.J. Crowder May 27 '15 at 15:21
1

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);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Praveen
  • 206
  • 2
  • 11
1

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bill Pope
  • 591
  • 8
  • 20