Losing this context in JavaScript when passing around members
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):
- Mythical methods
- You must remember
this
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
Javascript lost context when assigned to other variable
It depends on how a function is called. If a function is not referenced through being an attribute of an object (e.g. refToMethod
) then it will be assigned the "Global context" which is window
. However, when a function is an attribute of object (e.g. obj.method
), we refer to it as a method, and it is implicitly assigned the context of it's parent object.
JavaScript's context is unlike many languages in that you can override it easily using either .call()
or .apply()
. Furthermore, ECMAScript 5 introduced a new .bind()
method to allow you to create copies of methods which are always bound to the same context. See MDN for more.
var obj = new Class();
obj.method(); // 1;
var unbound = obj.method;
unbound(); // undefined;
// Call and Apply setting the context to obj.
unbound.apply(obj); // 1
unbound.call(obj); // 1;
// ECMAScript 5's bind
var bound = unbound.bind(obj);
bound(); // 1;
Javascript: Object context lost when calling private function
this
is always function scoped in JavaScript (unless you pass in a context explicitly using call()
or apply()
. Therefore, in your private functions, this
no longer refers to the same this
as in the parent scope. An idiomatic way of handling this in JavaScript is to assign this
to a self
var in the parent scope. E.g.,
var myStorage = function(mymode) {
var self = this;
var mode = mymode;
function privateFunctionA() {
console.log(self);
};
...
};
Regarding this snippet:
myStorage.prototype.publicFunc = function() {
console.log(this.mode); // does this work?
}
You will need to assign mode
to this
back in your constructor (instead of as a var
). So, the constructor would now become:
var myStorage = function(mymode) {
var self = this;
this.mode = mymode;
function privateFunctionA() {
// works
console.log(self.mode);
};
...
};
And this.mode
will also work in your .publicFunc()
in this case.
As one more stylistic note, constructor functions in JavaScript usually use proper camel case (i.e., MyStorage
).
JavaScript losing this context, function inside class function
this
isn't obtained from closure unless you use an arrow function, instead it as received by how the function is called.
Since c
is called directly, this here refers to undefined
.
You can declare c to be an arrow function to get this from the enclosing scope
class b { constructor(){ this.name = 'bar' } b1(){ console.log('here: ', this.name); const c = () => { console.log('inside c: ', this.name) } c(); } } let a = new b; a.b1();
Passing a prototype's function as parameter without losing the 'this' context
Use Function.prototype.bind
:
setTimeout( this.func.bind(this), 100 );
From Mozilla Developer Network:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this
: oThis || window,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
Class methods assigned to variable (alias) lose the value of this in Javascript
Because this
refers to method1
which does not have a v1
,
You can use bind in the constructor :
Bind creates a new function that will have
this
set to the first parameter passed to bind().
class Example1 { constructor(v1) { this.v1 = v1;
this.method1 = this.method1.bind(this); }
method1() { console.log("v1 value = ", this.v1) }}
const example1 = new Example1("value1");const alias1 = example1.method1;
example1.method1();alias1();
Object-method callback loses its binding in an event handler when passed as a parameter, but not when hard-coded
You're bind
ing the return value of cb
- try binding the function first then calling it:
cb.bind(this)();
How to access the correct `this` inside a callback
What you should know about this
this
(aka "the context") is a special keyword inside each function and its value only depends on how the function was called, not how/when/where it was defined. It is not affected by lexical scopes like other variables (except for arrow functions, see below). Here are some examples:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
To learn more about this
, have a look at the MDN documentation.
How to refer to the correct this
Use arrow functions
ECMAScript 6 introduced arrow functions, which can be thought of as lambda functions. They don't have their own this
binding. Instead, this
is looked up in scope just like a normal variable. That means you don't have to call .bind
. That's not the only special behavior they have, please refer to the MDN documentation for more information.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
Don't use this
You actually don't want to access this
in particular, but the object it refers to. That's why an easy solution is to simply create a new variable that also refers to that object. The variable can have any name, but common ones are self
and that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Since self
is a normal variable, it obeys lexical scope rules and is accessible inside the callback. This also has the advantage that you can access the this
value of the callback itself.
Explicitly set this
of the callback - part 1
It might look like you have no control over the value of this
because its value is set automatically, but that is actually not the case.
Every function has the method .bind
[docs], which returns a new function with this
bound to a value. The function has exactly the same behavior as the one you called .bind
on, only that this
was set by you. No matter how or when that function is called, this
will always refer to the passed value.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
In this case, we are binding the callback's this
to the value of MyConstructor
's this
.
Note: When a binding context for jQuery, use jQuery.proxy
[docs] instead. The reason to do this is so that you don't need to store the reference to the function when unbinding an event callback. jQuery handles that internally.
Set this
of the callback - part 2
Some functions/methods which accept callbacks also accept a value to which the callback's this
should refer to. This is basically the same as binding it yourself, but the function/method does it for you. Array#map
[docs] is such a method. Its signature is:
array.map(callback[, thisArg])
The first argument is the callback and the second argument is the value this
should refer to. Here is a contrived example:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
Note: Whether or not you can pass a value for this
is usually mentioned in the documentation of that function/method. For example, jQuery's $.ajax
method [docs] describes an option called context
:
This object will be made the context of all Ajax-related callbacks.
Common problem: Using object methods as callbacks/event handlers
Another common manifestation of this problem is when an object method is used as callback/event handler. Functions are first-class citizens in JavaScript and the term "method" is just a colloquial term for a function that is a value of an object property. But that function doesn't have a specific link to its "containing" object.
Consider the following example:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
The function this.method
is assigned as click event handler, but if the document.body
is clicked, the value logged will be undefined
, because inside the event handler, this
refers to the document.body
, not the instance of Foo
.
As already mentioned at the beginning, what this
refers to depends on how the function is called, not how it is defined.
If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
The solution is the same as mentioned above: If available, use .bind
to explicitly bind this
to a specific value
document.body.onclick = this.method.bind(this);
or explicitly call the function as a "method" of the object, by using an anonymous function as callback / event handler and assign the object (this
) to another variable:
var self = this;
document.body.onclick = function() {
self.method();
};
or use an arrow function:
document.body.onclick = () => this.method();
Functional use of Array.includes causes TypeError
You lose the context when you pass that function as is: when it's invoked as a callback, value of this
inside it is undefined
in strict mode (and global object in non-strict mode), not [1]
. To address this issue, you may fix the context:
[1].some([].includes.bind([1]))
Note that it doesn't matter which array is used to access includes
function; you might as well write that as...
[1].some( Array.prototype.includes.bind([1]) )
That's be a bit less concise, but a bit more efficient (as no immediate array is created). Still, it almost never should be a bottleneck; thus you should better optimize for readability.
Unfortunately, this won't be enough. See, Array.includes() uses two parameters:
arr.includes(searchElement[, fromIndex])
... and Array.some() does supply it with those two (even three in fact, but only two are used by includes
). That's why this...
[1,2,3].some([].includes.bind([1])); // true
... works, but this...
[2,1,3].some([].includes.bind([1])); // false
... doesn't: the lookups in [1]
array start from 0th, 1st, 2nd elements - and apparently fail after the first one.
To fix this, you might either create a function that takes exactly one argument with something like lodash's _.unary:
[2,1,3].some(_.unary([].includes.bind([1]))) // now we're talking!
... or bite the bullet and use arrow function instead. Note that you can still use a function with a bound context here:
const checker = [].includes.bind([1]);
[2,1,3].some(el => checker(el));
... to make this more flexible.
Related Topics
How to Write a Script to Edit a JSON File
What Is the "Best" Way to Get and Set a Single Cookie Value Using JavaScript
Programming Language Independent Model Validation
Passing Functions to Settimeout in a Loop: Always the Last Value
How Does a Function in a Loop (Which Returns Another Function) Work
Youtube Iframe Player API - Onstatechange Not Firing
JavaScript Parseint() with Leading Zeros
Check If One Date Is Between Two Dates
Compare Two JavaScript Arrays and Remove Duplicates
Capture Browser Console Logs with Capybara
Conditionalpanel JavaScript Conditions in Shiny: Is There R %In% Operator in JavaScript
How to Save HTML Page as PDF Using JavaScript or Jquery
Mediastream Capture Canvas and Audio Simultaneously
Faye Ruby Client Is Not Working