I have a constructor function which registers an event handler:
function MyConstructor(data, transport) {_x000D_
this.data = data;_x000D_
transport.on('data', function () {_x000D_
alert(this.data);_x000D_
});_x000D_
}_x000D_
_x000D_
// Mock transport object_x000D_
var transport = {_x000D_
on: function(event, callback) {_x000D_
setTimeout(callback, 1000);_x000D_
}_x000D_
};_x000D_
_x000D_
// called as_x000D_
var obj = new MyConstructor('foo', transport);
_x000D_
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 an other 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?
This question is related to
javascript
callback
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 which 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 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/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: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Method3: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: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
We can not bind this to setTimeout()
, as it always execute with global object (Window), if you want to access this
context in the callback function then by using bind()
to the callback function we can achieve as:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
First, you need to have a clear understanding of scope
and behaviour of this
keyword in the context of scope
.
this
& scope
:
there are two types of scope in javascript. They are :
1) Global Scope
2) 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.variable declared inside a function cannot be accessed from outside world normally.this
keyword in global scope refers to the window object.this
inside 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 outer function"this" keyword refers to window object - -
- callback() // "this" inside callback also refers window object -
- } -
- -
- function callback_function(){ -
- -
- // function to be passed as callback -
- -
- // here "THIS" refers to 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
,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
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()
}
first one bind this
with callback inside the method itself.And for the second one 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
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)
Similar to call
, first argument of apply
refers to the object that will be indicated by this
keyword.
sayNameVersion4
uses apply
to manipulate this
to refer to person object
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
and it is called like the following.Simply the callback is passed,
p1.sayNameVersion4(niceCallback)
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: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
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));
bind()
function. bind()
functionfunction 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));
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
The question revolves around how this
keyword behaves in javascript. this
behaves differently as below,
this
is usually determined by a function execution context.this
refers to the global object (the window
object).this
will be undefined
as in strict mode, global object refers to undefined
in place of the window
object.call()
, bind()
, and apply()
new
keyword is used (a constructor), this is bound to the new object being created.this
— instead, this
is bound lexically (i.e. based on the original context)As most of the answers suggest, we can use 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:
Currently there is another approach possible if classes are used in code.
With support of class fields it's possible to make it next way:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // this refers to correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
For sure under the hood it's all old good arrow function that bind context but in this form it looks much more clear that explicit binding.
Since it's Stage 3 Proposal you will need babel and appropriate babel plugin to process it as for now(08/2018).
this
in JS:The value of this
in JS 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':
this
is the object left of the dot of the function which is calledthis
inside a function is often the global object (global
in node, window
in browser). I wouldn't recommend using the this
keyword here because it is less explicit than using something like window
!Function.prototype.bind()
a function that can fix the value of this
. These are exceptions of the rule but are really helpful to fix the value of this
.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 1 by 1 (ignoring the first log starting from the second):
this
is obj2
because of the left of the dot rule, we can see how test1
is called obj2.test1();
. obj2
is left of the dot and thus the this
value.obj2
is left of the dot, test2
is bound to obj1
via the bind()
method. So the this
value is obj1
.obj2
is left of the dot from the function which is called: obj2.test3()
. Therefore obj2
will be the value of this
.obj2.test4()
obj2
is left of the dot. However, arrow functions don't have their own this
binding. Therefore it will bind to the this
value of the outer scope which is the module.exports
an object which was logged in the beginning.this
by using the call
function. Here we can pass in the desired this
value as an argument, which is obj2
in this case.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.
So 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.
I was facing 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. Hope this helps the Ngx
line chart, users.
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
}
Source: Stackoverflow.com