admin管理员组文章数量:1126295
I have a constructor function which registers an event handler:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
I have a constructor function which registers an event handler:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
However, I'm not able to access the data
property of the created object inside the callback. It looks like this
does not refer to the object that was created, but to another one.
I also tried to use an object method instead of an anonymous function:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
but it exhibits the same problems.
How can I access the correct object?
Share Improve this question edited Jul 25, 2021 at 20:19 Peter Mortensen 31.6k22 gold badges109 silver badges133 bronze badges asked Nov 29, 2013 at 6:13 Felix KlingFelix Kling 816k180 gold badges1.1k silver badges1.2k bronze badges 4- Related: Preserving a reference to “this” in JavaScript prototype functions – Bergi Commented Mar 1, 2014 at 12:46
- Related: “this” keyword in event methods when using JavaScript prototype object – Bergi Commented Sep 9, 2014 at 20:43
- 5 Useful TypeScript page about this, mostly applicable to JS too. – Ondra Žižka Commented Feb 1, 2017 at 21:49
- 8 @strattonn: Self-answering questions is an option in the UI and encouraged: stackoverflow.blog/2011/07/01/…. I did this a couple of times for issues that came up over and over again to provide a canonical answer. The problem with canonical Q&A is that existing questions are often not phrased in a general enough way that focuses on the core problem. – Felix Kling Commented Nov 21, 2020 at 19:15
16 Answers
Reset to default 2308What 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();
Here are several ways to access the parent context inside a child context -
- You can use the
bind()
function. - Store a reference to context/this inside another variable (see the below example).
- Use ES6 Arrow functions.
- Alter the code, function design, and architecture - for this you should have command over design patterns in JavaScript.
1. Use the bind()
function
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
If you are using Underscore.js - http://underscorejs.org/#bind
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
2. Store a reference to context/this inside another variable
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
3. Arrow function
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
It's all in the "magic" syntax of calling a method:
object.property();
When you get the property from the object and call it in one go, the object will be the context for the method. If you call the same method, but in separate steps, the context is the global scope (window) instead:
var f = object.property;
f();
When you get the reference of a method, it's no longer attached to the object. It's just a reference to a plain function. The same happens when you get the reference to use as a callback:
this.saveNextLevelData(this.setAll);
That's where you would bind the context to the function:
this.saveNextLevelData(this.setAll.bind(this));
If you are using jQuery you should use the $.proxy
method instead, as bind
is not supported in all browsers:
this.saveNextLevelData($.proxy(this.setAll, this));
You should know about "this" Keyword.
As per my view you can implement "this" in three ways (Self|Arrow function|Bind Method)
A function's this
keyword behaves a little differently in JavaScript compared to other languages.
It also has some differences between strict mode and non-strict mode.
In most cases, the value of this is determined by how a function is called.
It can't be set by assignment during execution, and it may be different each time the function is called.
ES5 introduced the bind() method to set the value of a function's this
regardless of how it's called,
And ES2015 introduced arrow functions that don't provide their own this
binding (it retains this value of the enclosing lexical context).
Method1: Self - Self is being used to maintain a reference to the original this even as the context is changing. It's a technique often used in event handlers (especially in closures).
Reference: this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
Method2: Arrow function - An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords.
Arrow function expressions are ill-suited as methods, and they cannot be used as constructors.
Reference: Arrow function expressions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Method 3: Bind - The bind() method creates a new function that, when called, has its this
keyword set to the provided value with a given sequence of arguments preceding any provided when the new function is called.
Reference: Function.prototype.bind()
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
The trouble with "context"
The term "context" is sometimes used to refer to the object referenced by this. Its use is inappropriate, because it doesn't fit either semantically or technically with ECMAScript's this.
"Context" means the circumstances surrounding something that adds meaning, or some preceding and following information that gives extra meaning. The term "context" is used in ECMAScript to refer to execution context, which is all the parameters, scope, and this within the scope of some executing code.
This is shown in ECMA-262 section 10.4.2:
Set the ThisBinding to the same value as the ThisBinding of the calling execution context
Which clearly indicates that this is part of an execution context.
An execution context provides the surrounding information that adds meaning to the code that is being executed. It includes much more information than just the thisBinding.
The value of this isn't "context". It's just one part of an execution context. It's essentially a local variable that can be set by the call to any object and in strict mode, to any value at all.
First, you need to have a clear understanding of scope
and behaviour of the this
keyword in the context of scope
.
this
& scope
:
There are two types of scope in JavaScript. They are:
Global Scope
Function Scope
In short, global scope refers to the window object. Variables declared in a global scope are accessible from anywhere.
On the other hand, function scope resides inside of a function. A variable declared inside a function cannot be accessed from the outside world normally.
The this
keyword in the global scope refers to the window object. this
inside a function also refers to the window object. So this
will always refer to the window until we find a way to manipulate this
to indicate a context of our own choosing.
--------------------------------------------------------------------------------
- -
- Global Scope -
- (globally "this" refers to window object) -
- -
- function outer_function(callback){ -
- -
- // Outer function scope -
- // Inside the outer function, the "this" keyword -
- // refers to window object -
- callback() // "this" inside callback also refers to the window object -
- } -
- -
- function callback_function(){ -
- -
- // Function to be passed as callback -
- -
- // Here "THIS" refers to the window object also -
- } -
- -
- outer_function(callback_function) -
- // Invoke with callback -
- -
--------------------------------------------------------------------------------
Different ways to manipulate this
inside callback functions:
Here I have a constructor function called Person. It has a property called name
and four method called sayNameVersion1
, sayNameVersion2
, sayNameVersion3
, and sayNameVersion4
. All four of them has one specific task. Accept a callback and invoke it. The callback has a specific task which is to log the name property of an instance of Person constructor function.
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// Function to be used as callback
var parentObject = this
console.log(parentObject)
}
Now let's create an instance from person constructor and invoke different versions of sayNameVersionX
(X refers to 1,2,3,4) method with niceCallback
to see how many ways we can manipulate the this
inside callback to refer to the person
instance.
var p1 = new Person('zami') // Create an instance of Person constructor
bind:
What bind do is to create a new function with the this
keyword set to the provided value.
sayNameVersion1
and sayNameVersion2
use bind to manipulate this
of the callback function.
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
The first one binds this
with a callback inside the method itself. And for the second one, the callback is passed with the object bound to it.
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
call:
The first argument
of the call
method is used as this
inside the function that is invoked with call
attached to it.
sayNameVersion3
uses call
to manipulate the this
to refer to the person object that we created, instead of the window object.
this.sayNameVersion3 = function(callback){
callback.call(this)
}
And it is called like the following:
p1.sayNameVersion3(niceCallback)
apply:
Similar to call
, the first argument of apply
refers to the object that will be indicated by the this
keyword.
sayNameVersion4
uses apply
to manipulate this
to refer to a person object
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
And it is called like the following. Simply the callback is passed,
p1.sayNameVersion4(niceCallback)
We can not bind this to setTimeout()
, as it always executes with the global object (Window). If you want to access the this
context in the callback function then by using bind()
to the callback function, we can achieve it as:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
The question revolves around how the this
keyword behaves in JavaScript. this
behaves differently as below,
- The value of
this
is usually determined by a function execution context. - In the global scope,
this
refers to the global object (thewindow
object). - If strict mode is enabled for any function then the value of
this
will beundefined
as in strict mode, global object refers toundefined
in place of thewindow
object. - The object that is standing before the dot is what the
this
keyword will be bound to. - We can set the value of this explicitly with
call()
,bind()
, andapply()
- When the
new
keyword is used (a constructor), this is bound to the new object being created. - Arrow functions don’t bind
this
— instead,this
is bound lexically (i.e., based on the original context)
As most of the answers suggest, we can use the arrow function or bind()
Method or Self var. I would quote a point about lambdas (arrow function) from Google JavaScript Style Guide
Prefer using arrow functions over f.bind(this), and especially over goog.bind(f, this). Avoid writing const self = this. Arrow functions are particularly useful for callbacks, which sometimes pass unexpectedly additional arguments.
Google clearly recommends using lambdas rather than bind or const self = this
So the best solution would be to use lambdas as below,
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
References:
- https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
- arrow-functions-vs-bind
Currently there is another approach possible if classes are used in code.
With support of class fields, it's possible to make it the following way:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // This refers to the correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
For sure under the hood it's all the old good arrow function that binds context, but in this form it looks much more clear that explicit binding.
Since it's a Stage 3 Proposal, you will need Babel and appropriate Babel plugin to process it as for now (08/2018).
Another approach, which is the standard way since DOM2 to bind this
within the event listener, that let you always remove the listener (among other benefits), is the handleEvent(evt)
method from the EventListener
interface:
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
Detailed information about using handleEvent
can be found here: DOM handleEvent: a cross-platform standard since year 2000
I was facing a problem with Ngx
line chart xAxisTickFormatting
function which was called from HTML like this: [xAxisTickFormatting]="xFormat"
.
I was unable to access my component's variable from the function declared. This solution helped me to resolve the issue to find the correct this.
Instead of using the function like this:
xFormat (value): string {
return value.toString() + this.oneComponentVariable; //gives wrong result
}
Use this:
xFormat = (value) => {
// console.log(this);
// now you have access to your component variables
return value + this.oneComponentVariable
}
this
in JavaScript:
The value of this
in JavaScript is 100% determined by how a function is called, and not how it is defined. We can relatively easily find the value of this
by the 'left of the dot rule':
- When the function is created using the function keyword the value of
this
is the object left of the dot of the function which is called - If there is no object left of the dot then the value of
this
inside a function is often the global object (global
in Node.js andwindow
in a browser). I wouldn't recommend using thethis
keyword here because it is less explicit than using something likewindow
! - There exist certain constructs like arrow functions and functions created using the
Function.prototype.bind()
a function that can fix the value ofthis
. These are exceptions of the rule, but they are really helpful to fix the value ofthis
.
Example in Node.js
module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);
const obj1 = {
data: "obj1 data",
met1: function () {
console.log(this.data);
},
met2: () => {
console.log(this.data);
},
};
const obj2 = {
data: "obj2 data",
test1: function () {
console.log(this.data);
},
test2: function () {
console.log(this.data);
}.bind(obj1),
test3: obj1.met1,
test4: obj1.met2,
};
obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);
Output:
Let me walk you through the outputs one by one (ignoring the first log starting from the second):
this
isobj2
because of the left of the dot rule, we can see howtest1
is calledobj2.test1();
.obj2
is left of the dot and thus thethis
value.- Even though
obj2
is left of the dot,test2
is bound toobj1
via thebind()
method. Thethis
value isobj1
. obj2
is left of the dot from the function which is called:obj2.test3()
. Thereforeobj2
will be the value ofthis
.- In this case:
obj2.test4()
obj2
is left of the dot. However, arrow functions don't have their ownthis
binding. Therefore it will bind to thethis
value of the outer scope which is themodule.exports
an object which was logged in the beginning. - We can also specify the value of
this
by using thecall
function. Here we can pass in the desiredthis
value as an argument, which isobj2
in this case.
Some other people have touched on how to use the .bind() method, but specifically here is how you can use it with .then() if anyone is having trouble getting them to work together:
someFunction()
.then(function(response) {
//'this' wasn't accessible here before but now it is
}.bind(this))
As mentioned in the comments, an alternative would be to use an arrow function that doesn't have its own 'this' value
someFunction()
.then((response)=>{
//'this' was always accessible here
})
You can use arrow function to avoid the issue with this .
const functionToTest = (dataToSet , transport) => {
this.dataToSet = dataToSet ;
transport.on('dataToSet ', () => {
console.log(this.dataToSet);
});
}
Anser for year 2024
A lot of information here is quite obsolete. This answer is focused on how to resolve in 2024+
- Use instances when calling function
- never take function "out of its instance". If you need to call that function later, just keep the whole instance, for example like this:
class A {
constructor(id) {
this.id = id;
}
someFunction(){
console.log(this);
}
}
const a1 = new A(5);
const a2 = new A(20);
const arr = [a1, a2];
// some business logic
arr.forEach(a => a.someFunction());
- Do not overuse
this
Do not use this
and classes if not necessary. When you require files like repositories, controllers, services, it by default creates singletons, which is something you want (its basically like @Autowired in Java, but native). And singletons do not require classes and "this", whatever you need to define, define on the root of the file. Then just export functions/variables you want. If you dont use this
, you cannot have problem with this
.
For example somethingService.js
can look similar to the code below:
const someDefaultValue = 10;
export function doSomething() {
console.log(someDefaultValue);
}
Then you dont have to think about using doSomething()
as somethingService.doSomething()
or doSomething()
directly. (it will work the same)
- Use apply/call/bind
I am personally not fan of using apply
(or call
or bind
which is basically the same, just called a bit differently) extensively, from my experience it can bring even more chaos into code. But there can be situations where you might need it, so I will list it here. You can basically "inject" this
with whatever object you like inside function.
class Simple {
constructor(id) {
this.id = id;
}
showThis(text) {
console.log(this, ` ** ${text} **`)
}
}
const simple = new Simple(25);
const showThisFn = simple.showThis;
showThisFn("using it without apply");
showThisFn.apply(simple, ["using it with apply"])
showThisFn.call(simple, "using it with call - basically same as apply, only syntactic sugar difference today")
boundShowThisFn = showThisFn.bind(simple);
boundShowThisFn("now it is bound with the function, so you dont have to specify it when calling it");
- Use Arrow Function => correctly
If you have function inside function and you use this
, the arrow function will pass the this
context in the way you would expect. Using function
keyword will change the context of this
.
(basically use arrow functions whenever it is possible and you are safe)
class A {
constructor(baseValue) {
this.baseValue = baseValue;
}
doMathArrowFunction(arr){
arr.forEach(item => {
console.log(`Arrow function: item is ${item} and this?.baseValue is ${this?.baseValue}`);
console.log(item + this?.baseValue);
});
}
doMathOldWay(arr) {
arr.forEach(function(item) {
console.log(`Old fashion function: item is ${item} and this?.baseValue is ${this?.baseValue}`);
console.log(item + this?.baseValue);
});
}
}
const x = [1,2];
const a = new A(10);
a.doMathArrowFunction(x);
a.doMathOldWay(x);
In past (before Arrow function existed) it was often resolved with self/that
keywords. If you encounter it and want to know more - check accepted answer in this thread.
This is how I solved the problem
class myClass
{
constructor(parent)
{
this.callback = (function() {
this.callbackFunctionOfParent();
}).bind(parent);
}
callCallback() {
this.callback();
}
}
class Class2
{
constructor()
{
this.Name = "CLASS 2";
this.test = new myClass(this);
this.test.callCallback();
}
callbackFunctionOfParent()
{
console.log("parent is: " + this.Name);
}
}
var c2 = new Class2;
本文标签: javascriptHow to access the correct this inside a callbackStack Overflow
版权声明:本文标题:javascript - How to access the correct `this` inside a callback - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736666185a1946672.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论