This question already has an answer here:
- How does the “this” keyword work? 21 answers
I'm trying to learn about this
, and it's confusing me a bit here:
var randomFunction = function(callback) {
var data = 10;
callback(data);
};
var obj = {
initialData: 20,
sumData: function(data) {
var sum = this.initialData + data;
console.log(sum);
},
prepareRandomFunction: function() {
randomFunction(this.sumData.bind(this));
}
};
obj.prepareRandomFunction();
Is this
designed to set itself where it is first rendered in code? For instance, in my example I'm successfully using it to refer to obj
and also binding the function to obj
, but since this
is being passed as a callback function, what is stopping it from being set as randomFunction
(i.e. what's stopping it from literally passing "this.sumData.bind(this)" so that this
is set to randomFunction
when it gets called from there)?
I'm a noob trying to learn. Thanks.
Updated
I'm not exactly asking how this works generally (I don't think). I'm mainly curious to know why this
gets set where I define it as the argument of my randomFunction
call, and not where callback
gets called within randomFunction
. I could be wrong, but if I were to swap this.sumData.bind(this)
with the callback(data)
that I currently have I think I would get a different result. Is that because callback
is a reference to this.sumData.bind(this)
when it was first defined (and where this
is obj
)?
I think I've learned through this scenario that this
is set when it's executed. It's not passed as a argument to be set later when the argument is called down the line.
No. It's set by how a function is called or by using bind.
Because the function is called using:
which sets this in the function to obj.
in:
since prepareRandomFunction has been called with obj as this, then the value of this within obj.sumData is set to obj, and the call to randomFunction effectively resolves to:
The fact that you've set this to obj in the call. If you want this to be randomFunction, you'll need to set it to that. randomFunction has no intrinsic value for this, it is set by how the function is called (or use of bind).
Because this isn't randomFunction when that code is executed.
So:
calls prepareRandomFunction with this set to obj, which then does (replacing this with obj:
Within randomFunction, this hasn't been set so it defaults to the global object (irrelevant really as it's not used here). Then:
Which creates a local variable data with a value of 10, then calls sumData with its this set to obj and passes the value of data. Then:
and since this is obj, the assignment effectively resolves to:
which is 30.
this
inside a function call gets set according to how a function is called. There are six main ways thatthis
gets set.Normal Function Call: In a normal function call such as
foo()
,this
is set to either the global object (which iswindow
in a browser) or toundefined
(in Javascript's strict mode).Method Call: If a method is called such as
obj.foo()
, thenthis
is set toobj
inside the function..apply() or .call(): If
.apply()
or.call()
is used, thenthis
is set according to what is passed to.apply()
or.call()
. For example, you could dofoo.call(myObj)
and causethis
to be set tomyObj
inside offoo()
for that particular function call.Using new: If you call a function with
new
such asnew foo()
, then a new object is created and the constructor functionfoo
is called withthis
set to the newly created object.Using .bind(): When using
.bind()
a new stub function is returned from that call that internally uses.apply()
to set thethis
pointer as was passed to.bind()
. FYI, this isn't really a different case because.bind()
can be implemented with.apply()
.Using ES6 Fat Arrow Function Defining a function via the arrow syntax in ES6+ will bind the current lexical value of
this
to it. So, no matter how the function is called elsewhere, thethis
value will be set by the interpreter to the value thatthis
has when the function was defined. This is completely different than all other function calls.There's sort of a seventh method, via a callback function, but it isn't really its own scheme, but rather the function calling the callback uses one of the above schemes and that determines what the value of
this
will be when the callback is called. You have to consult either the documentation or the code for the calling function or test it yourself to determine whatthis
will be set to in a callback.What is important to understand in Javascript is that every single function or method call in Javascript sets a new value for
this
. And, which value is set is determined by how the function is called.So, if you pass a method as a plain callback, that method will not, by default, get called as
obj.method()
and thus will not have the right value ofthis
set for it. You can use.bind()
to work around that issue.It's also useful to know that some callback functions (such as DOM event handlers) are called with a specific value of
this
as set by the infrastructure that calls the callback function. Internally, they all use.call()
or.apply()
so this isn't a new rule, but is something to be aware of. The "contract" for a callback function may include how it sets the value ofthis
. If it does not explicitly set the value ofthis
, then it will be set according to rule #1.In ES6, calling a function via an arrow function, maintains the current lexical value of
this
. Here's an example of the array function maintaining the lexicalthis
from MDN:Your example of
obj.prepareRandomFunction();
is rule #2 above sothis
will be set toobj
.Your example of
randomFunction(this.sumData.bind(this))
is rule #1 above sothis
inside ofrandomFunction
will be set to the global object orundefined
(if in strict mode).Since randomFunction is calling a callback function which itself used
.bind()
, then the value ofthis
inside the callback function when it is called will be set to the value ofthis
that was passed to.bind()
inthis.sumData.bind(this)
as via rule #5 above..bind()
actually creates a new function who's job it is to call the original function AFTER setting a custom value ofthis
.Here are a couple other references on the topic:
How to avoid "this" refering to the DOM element, and refer to the object
A better understanding of this
How does the "this" keyword work?
Note, that with the use of
.apply()
or.call()
or.bind()
, you can create all sorts of somewhat odd things and sometimes quite useful things that could never be done in something like C++. You can take any function or method in the world and call it as if it were a method of some other object.For example, this is often used to make a copy of the items in the
arguments
object into an array:or similarly:
This takes the array's
.slice()
method and calls it, but supplies it with an arguments object as thethis
pointer. Thearguments
object (though not an actual array), has just enough array-like functionality that the.slice()
method can operate on it and it ends up making a copy of thearguments
items into an actual array which can then be operated on directly with real array operations. This type of chicanery can't be done willy-nilly. If the array.slice()
method relied on other array methods that are not present on thearguments
object, then this trick would not work, but since it only relies on[]
and.length
, both of which thearguments
object has, it does actually work.So, this trick can be used to "borrow" methods from any object and apply them to another object as long as the object you are applying them to supports whatever methods or properties that the method actually uses. This can't be done in C++ because methods and properties are "hard bound" at compile time (even virtual methods in C++ are bound to a specific v-table location established at compile time), but can be easily done in Javascript because properties and methods are looked up live at runtime via their actual name so any object that contains the right properties and methods will work with any method that operates on those.
Step by step.
1)
this
insideprepareRandomFunction
isobj
2)
randomFunction
takes a function:3) that function gets called:
notice
callback
is called without a dot, which means it has no value forthis
, which meansthis
is the global object (orundefined
in strict mode).4)
sumData
gets called:this
is the global object,initialData
doesn't exist, you addundefined
todata
. Unexpected results.Solution: bind
this
permanently:4)
sumData
runs,this
isobj
,obj.initialData
is20
. It works.this is a self - reference of an object. When you create an object, either by using the new - operator , declaring it as an object - literal or
by calling one of the built -in's of JS, this will gonna be assigned to it.
But as the scoping of JS is dynamic and rather non - transient, you usually cannot rely too much on the this - object. This is why you often see constructs such as oThis : this or var o