JavaScript: Access 'this' when calling function stored in variable
Your problem is this line:
var func = foo.funcs[1];
JavaScript determines the value of this
based on how a function is called. If you use dot notation, such as foo.funcs[1]();
then the value of this
will associated with the foo
object. But when you run func()
, that's just a plain function and this
will have the default value of undefined
.
It would be worth your time to read the two chapters of You Don't Know JS that discuss this
. It should take less than an hour to learn, and you'll be way ahead of most JS programmers once you learn it.
The rules might not make sense until you read the chapter, but they are summarized below:
Determining the
this
binding for an executing function requires finding the direct call-site of that function. Once examined, four rules can be applied to the call-site, in this order of precedence:Called with new? Use the newly constructed object.
Called with call or apply (or bind)? Use the specified object.
Called with a context object owning the call? Use that context object.
Default: undefined in strict mode, global object otherwise.
Based on the above rules, the code below is the simplest way you could get it to work the way you are expecting it to:
'use strict';
function Foo() {
this.funcs = {
1: this.func1,
2: this.func2,
}
}
Foo.prototype.func1 = function() {
this.prop = 1;
console.log('called func1. this.prop =', this.prop);
}
Foo.prototype.func2 = function() {
this.prop = 2;
console.log('called func2. this.prop =', this.prop);
}
const foo = new Foo();
foo.funcs[1]();
There are a few ways to achieve what you require, however the most robust approach is to bind()
each function to the instance of Foo()
that is being instantiated.
This can be done by passing this
to bind()
of each function:
this.func1.bind(this)
Using bind()
in this way ensures that this
, for func1
and func2
is defined as the instance of Foo()
. This in turn ensures that this.prop
can be accessed and assigned as expected:
'use strict';
function Foo() {
this.funcs = {
/* Bind the functions to this Foo() instance */
1: this.func1.bind(this),
2: this.func2.bind(this),
}
}
Foo.prototype.func1 = function() {
this.prop = 1;
console.log('called func1. this.prop =', this.prop);
}
Foo.prototype.func2 = function() {
this.prop = 2;
console.log('called func2. this.prop =', this.prop);
}
const foo = new Foo();
var func = foo.funcs[1];
func();
foo.funcs[2]();
Another key thing to note is the bind()
based approach above ensures that, if you acquire and call a reference to one of the functions on the funcs
field as shown in your original post, that it will work as expected:
/* As per original post - doing this is not possible without .bind() */
var func = foo.funcs[1];
func();
Without the use of bind()
, this method of acquiring and calling func
will fail due to func
not being bound to the instance of Foo
.