47

I basically have an object, extended with a function through its prototype. Inside that function, another function exists, however when using this in this nested function, it does not seem to refer to the object, but the function.

For example,

var sampleObject = function() {
 this.foo = 123;
}

sampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 }
 return nested();
}

var test = new sampleObject();

window.alert(test.getFoo()); // undefined

The this.foo does not refer to the 123 value, but is undefined as this refers to the nested function, in which no foo exists. How can I access the 123 value from the nested function?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
pimvdb
  • 151,816
  • 78
  • 307
  • 352

8 Answers8

41
sampleObject.prototype.getFoo = function() {
 var me = this;
 var nested = function() {
  return me.foo;
 }
 return nested;
}

By saving the value of this in a local variable, you make it explicitly part of the lexical context for that function and for all nested function scopes. Thus, on the call to "nested", that inner function will have its own scope (it's own this value), but it can still refer to the variable "me" in the enclosing scope.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • and what will happen if I do: `var foo = new sampleObject();` `$(window).on('scroll', foo.getFoo );` – vsync Oct 25 '12 at 05:27
  • @vsync that should be fine - if you're having problems with something like that, I suggest that you open a whole new question with the code sample that's causing difficulty. – Pointy Oct 25 '12 at 13:19
  • 1
    but that isn't fine, because by using a function reference we loose the `this` that is so vital to the prototyping, so `var me = this;` in your example will point to the `window` Object – vsync Oct 25 '12 at 20:31
  • 1
    @vsync ah OK sorry I read your question incorrectly. Yes in that case you'd need to either wrap the call in an anonymous function or else use `.bind()` or something to do it for you. – Pointy Oct 25 '12 at 20:40
  • 1
    just a note, here in the example above nested function is a closure, and me will be bind to the function even after the return. me will be in the closure scope of nested. And nested will just work fine, because it have me part of it, and it reference the sampleObject instance – Mohamed Allal Jan 13 '18 at 18:12
14

In your example "this" refers to the window object because you didn't specify another context when you call the nested function and you get undefind because window.foo is undefined.

You can fix this in 3 ways.

1 - Use a variable to store the outside this - most used method

sampleObject.prototype.getFoo = function() {
 var _this = this;
 var nested = function() {
  return _this.foo;
 }
 return nested();
}

2 - Use the bind method which bind the outer "this" to the inner one

sampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 }.bind(this);
 return nested();
}

3 - Use the call method which can pass the context to the function

SampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 };
 return nested.call(this);
}
Marius Lazar
  • 391
  • 3
  • 5
8

The common work around for that is to use closure

sampleObject.prototype.getFoo = function() {
  var _this = this; 
  var nested = function() {
    return _this.foo;
   }
   return nested();
}

Some libraries add methods to automate this

Community
  • 1
  • 1
Hemlock
  • 6,130
  • 1
  • 27
  • 37
7

tl;dr

Use arrow functions. They are available since ECMAScript 6:

var sampleObject = function() {
  this.foo = 123;
}

sampleObject.prototype.getFoo = function() {
  var nested = () => { // Changed this line.
    return this.foo;
  }
  return nested();
}

var test = new sampleObject();

window.alert(test.getFoo());

Explanation

This is one of the main advantages of arrow functions. Your case is described in the section: No binding of this. The refference states:

Until arrow functions, every new function defined its own this value [...] An arrow function does not create its own this context, so this has the original meaning from the enclosing context.

totymedli
  • 29,531
  • 22
  • 131
  • 165
4

Apart from declaring it to var _this = this, I also see codes doing var that = this or var self = this.

Knowing your variable's scope is important as it might raises unexpected result.

Lionel Chan
  • 7,894
  • 5
  • 40
  • 69
  • if you are doing a large project come up with a convention. ie always use var that = this; or self or what ever – Zachary K Jan 16 '11 at 08:50
  • For me, `that` is usually the variable in a prototype function, e.g. `foo.prototype.add = function(that) {return this + that}`, so I prefer `self` or `me`. – pimvdb Jan 16 '11 at 11:38
1

This is an old question, but I give another solution for the sake of completeness. Another approach involves function binding.

sampleObject.prototype.getFoo = function() {
 var nested = function() {
  return this.foo;
 }
 return nested.bind(this)();
}
c.bear
  • 1,197
  • 8
  • 21
  • hey, this is cool.. so.. can you elaborate showing a function with a parameter.. I have a feeling it looks similar, but.. different – bkwdesign Jul 08 '16 at 22:57
1

An ES6 way of doing this would be to use an Arrow Function. Basically, when you use an arrow function, it does not create it's own "this" context. So, using "this" would then refer to the parent function's context. Here's how the code would look:

sampleObject.prototype.getFoo = function() {
    const nested = () => {
        return this.foo; //"this" refers to parent function's context
    }
    return nested;
}
Matthew Goodwin
  • 1,162
  • 12
  • 16
0

This is a known wart on JavaScript. The usual pattern is to assign this to another variable (often self) in the outer function, then access self from the inner funtction. This works.

Raph Levien
  • 5,088
  • 25
  • 24