How to see runtime binding of this in Javascript? [duplicate]
Solution 1:
this
is a keyword in JavaScript that is a property of an execution context. Its main use is in functions and constructors.
The rules for this
are quite simple (if you stick to best practices).
Technical description of this
in the specification
The ECMAScript standard defines this
via the abstract operation (abbreviated AO) ResolveThisBinding:
The [AO] ResolveThisBinding […] determines the binding of the keyword
this
using the LexicalEnvironment of the running execution context. [Steps]:
- Let envRec be GetThisEnvironment().
- Return ? envRec.GetThisBinding().
Global Environment Records, module Environment Records, and function Environment Records each have their own GetThisBinding method.
The GetThisEnvironment AO finds the current running execution context’s LexicalEnvironment and finds the closest ascendant Environment Record (by iteratively accessing their [[OuterEnv]] properties) which has a this binding (i.e. HasThisBinding returns true). This process ends in one of the three Environment Record types.
The value of this
often depends on whether code is in strict mode.
The return value of GetThisBinding reflects the value of this
of the current execution context, so whenever a new execution context is established, this
resolves to a distinct value. This can also happen when the current execution context is modified. The following subsections list the five cases where this can happen.
You can put the code samples in the AST explorer to follow along with specification details.
1. Global execution context in scripts
This is script code evaluated at the top level, e.g. directly inside a <script>
:
<script>
// Global context
console.log(this); // Logs global object.
setTimeout(function(){
console.log("Not global context");
});
</script>
When in the initial global execution context of a script, evaluating this
causes GetThisBinding to take the following steps:
The GetThisBinding concrete method of a global Environment Record envRec […] [does this]:
- Return envRec.[[GlobalThisValue]].
The [[GlobalThisValue]] property of a global Environment Record is always set to the host-defined global object, which is reachable via globalThis
(window
on Web, global
on Node.js; Docs on MDN). Follow the steps of InitializeHostDefinedRealm to learn how the [[GlobalThisValue]] property comes to be.
2. Global execution context in modules
Modules have been introduced in ECMAScript 2015.
This applies to modules, e.g. when directly inside a <script type="module">
, as opposed to a simple <script>
.
When in the initial global execution context of a module, evaluating this
causes GetThisBinding to take the following steps:
The GetThisBinding concrete method of a module Environment Record […] [does this]:
- Return undefined.
In modules, the value of this
is always undefined
in the global context. Modules are implicitly in strict mode.
3. Entering eval code
There are two kinds of eval
calls: direct and indirect. This distinction exists since the ECMAScript 5th edition.
- A direct
eval
call usually looks likeeval(
…);
or(eval)(
…);
(or((eval))(
…);
, etc.).1 It’s only direct if the call expression fits a narrow pattern.2 - An indirect
eval
call involves calling the function referenceeval
in any other way. It could beeval?.(
…)
,(
…, eval)(
…)
,window.eval(
…)
,eval.call(
…,
…)
, etc. Givenconst aliasEval1 = eval; window.aliasEval2 = eval;
, it would also bealiasEval1(
…)
,aliasEval2(
…)
. Separately, givenconst originalEval = eval; window.eval = (x) => originalEval(x);
, callingeval(
…)
would also be indirect.
See chuckj’s answer to “(1, eval)('this') vs eval('this') in JavaScript?” and Dmitry Soshnikov’s ECMA-262-5 in detail – Chapter 2: Strict Mode (archived) for when you might use an indirect eval()
call.
PerformEval executes the eval
code. It creates a new declarative Environment Record as its LexicalEnvironment, which is where GetThisEnvironment gets the this
value from.
Then, if this
appears in eval
code, the GetThisBinding method of the Environment Record found by GetThisEnvironment is called and its value returned.
And the created declarative Environment Record depends on whether the eval
call was direct or indirect:
- In a direct eval, it will be based on the current running execution context’s LexicalEnvironment.
- In an indirect eval, it will be based on the [[GlobalEnv]] property (a global Environment Record) of the Realm Record which executed the indirect eval.
Which means:
- In a direct eval, the
this
value doesn’t change; it’s taken from the lexical scope that calledeval
. - In an indirect eval, the
this
value is the global object (globalThis
).
What about new Function
? — new Function
is similar to eval
, but it doesn’t call the code immediately; it creates a function. A this binding doesn’t apply anywhere here, except when the function is called, which works normally, as explained in the next subsection.
4. Entering function code
Entering function code occurs when calling a function.
There are four categories of syntax to invoke a function.
- The EvaluateCall AO is performed for these three:3
- Normal function calls
- Optional chaining calls
- Tagged templates
- And EvaluateNew is performed for this one:3
- Constructor invocations
The actual function call happens at the Call AO, which is called with a thisValue determined from context; this argument is passed along in a long chain of call-related calls. Call calls the [[Call]] internal slot of the function. This calls PrepareForOrdinaryCall where a new function Environment Record is created:
A function Environment Record is a declarative Environment Record that is used to represent the top-level scope of a function and, if the function is not an ArrowFunction, provides a
this
binding. If a function is not an ArrowFunction function and referencessuper
, its function Environment Record also contains the state that is used to performsuper
method invocations from within the function.
In addition, there is the [[ThisValue]] field in a function Environment Record:
This is the
this
value used for this invocation of the function.
The NewFunctionEnvironment call also sets the function environment’s [[ThisBindingStatus]] property.
[[Call]] also calls OrdinaryCallBindThis, where the appropriate thisArgument is determined based on:
- the original reference,
- the kind of the function, and
- whether or not the code is in strict mode.
Once determined, a final call to the BindThisValue method of the newly created function Environment Record actually sets the [[ThisValue]] field to the thisArgument.
Finally, this very field is where a function Environment Record’s GetThisBinding AO gets the value for this
from:
The GetThisBinding concrete method of a function Environment Record envRec […] [does this]:
[…]
3. Return envRec.[[ThisValue]].
Again, how exactly the this value is determined depends on many factors; this was just a general overview. With this technical background, let’s examine all the concrete examples.
Arrow functions
When an arrow function is evaluated, the [[ThisMode]] internal slot of the function object is set to “lexical” in OrdinaryFunctionCreate.
At OrdinaryCallBindThis, which takes a function F:
- Let thisMode be F.[[ThisMode]].
- If thisMode is lexical, return NormalCompletion(
undefined
). […]
which just means that the rest of the algorithm which binds this is skipped. An arrow function does not bind its own this value.
So, what is this
inside an arrow function, then? Looking back at ResolveThisBinding and GetThisEnvironment, the HasThisBinding method explicitly returns false.
The HasThisBinding concrete method of a function Environment Record envRec […] [does this]:
- If envRec.[[ThisBindingStatus]] is lexical, return false; otherwise, return true.
So the outer environment is looked up instead, iteratively. The process will end in one of the three environments that have a this binding.
This just means that, in arrow function bodies, this
comes from the lexical scope of the arrow function, or in other words (from Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?):
Arrow functions don’t have their own
this
[…] binding. Instead, [this identifier is] resolved in the lexical scope like any other variable. That means that inside an arrow function,this
[refers] to the [value ofthis
] in the environment the arrow function is defined in (i.e. “outside” the arrow function).
Function properties
In normal functions (function
, methods), this
is determined by how the function is called.
This is where these “syntax variants” come in handy.
Consider this object containing a function:
const refObj = {
func: function(){
console.log(this);
}
};
Alternatively:
const refObj = {
func(){
console.log(this);
}
};
In any of the following function calls, the this
value inside func
will be refObj
.1
refObj.func()
refObj["func"]()
refObj?.func()
refObj.func?.()
refObj.func``
If the called function is syntactically a property of a base object, then this base will be the “reference” of the call, which, in usual cases, will be the value of this
. This is explained by the evaluation steps linked above; for example, in refObj.func()
(or refObj["func"]()
), the CallMemberExpression is the entire expression refObj.func()
, which consists of the MemberExpression refObj.func
and the Arguments ()
.
But also, refObj.func
and refObj
play three roles, each:
- they’re both expressions,
- they’re both references, and
- they’re both values.
refObj.func
as a value is the callable function object; the corresponding reference is used to determine the this
binding.
The optional chaining and tagged template examples work very similarly: basically, the reference is everything before the ?.()
, before the ``
, or before the ()
.
EvaluateCall uses IsPropertyReference of that reference to determine if it is a property of an object, syntactically. It’s trying to get the [[Base]] property of the reference (which is e.g. refObj
, when applied to refObj.func
; or foo.bar
when applied to foo.bar.baz
). If it is written as a property, then GetThisValue will get this [[Base]] property and use it as the this value.
Note: Getters / Setters work the same way as methods, regarding this
. Simple properties don’t affect the execution context, e.g. here, this
is in global scope:
const o = {
a: 1,
b: this.a, // Is `globalThis.a`.
[this.a]: 2 // Refers to `globalThis.a`.
};
Calls without base reference, strict mode, and with
A call without a base reference is usually a function that isn’t called as a property. For example:
func(); // As opposed to `refObj.func();`.
This also happens when passing or assigning methods, or using the comma operator. This is where the difference between Reference Record and Value is relevant.
Note function j
: following the specification, you will notice that j
can only return the function object (Value) itself, but not a Reference Record. Therefore the base reference refObj
is lost.
const g = (f) => f(); // No base ref.
const h = refObj.func;
const j = () => refObj.func;
g(refObj.func);
h(); // No base ref.
j()(); // No base ref.
(0, refObj.func)(); // Another common pattern to remove the base ref.
EvaluateCall calls Call with a thisValue of undefined here. This makes a difference in OrdinaryCallBindThis (F: the function object; thisArgument: the thisValue passed to Call):
- Let thisMode be F.[[ThisMode]].
[…]
- If thisMode is strict, let thisValue be thisArgument.
- Else,
- If thisArgument is undefined or null, then
- Let globalEnv be calleeRealm.[[GlobalEnv]].
- […]
- Let thisValue be globalEnv.[[GlobalThisValue]].
- Else,
- Let thisValue be ! ToObject(thisArgument).
- NOTE: ToObject produces wrapper objects […].
[…]
Note: step 5 sets the actual value of this
to the supplied thisArgument in strict mode — undefined
in this case. In “sloppy mode”, an undefined or null thisArgument results in this
being the global this value.
If IsPropertyReference returns false, then EvaluateCall takes these steps:
- Let refEnv be ref.[[Base]].
- Assert: refEnv is an Environment Record.
- Let thisValue be refEnv.WithBaseObject().
This is where an undefined thisValue may come from: refEnv.WithBaseObject() is always undefined, except in with
statements. In this case, thisValue will be the binding object.
There’s also Symbol.unscopables
(Docs on MDN) to control the with
binding behavior.
To summarize, so far:
function f1(){
console.log(this);
}
function f2(){
console.log(this);
}
function f3(){
console.log(this);
}
const o = {
f1,
f2,
[Symbol.unscopables]: {
f2: true
}
};
f1(); // Logs `globalThis`.
with(o){
f1(); // Logs `o`.
f2(); // `f2` is unscopable, so this logs `globalThis`.
f3(); // `f3` is not on `o`, so this logs `globalThis`.
}
and:
"use strict";
function f(){
console.log(this);
}
f(); // Logs `undefined`.
// `with` statements are not allowed in strict-mode code.
Note that when evaluating this
, it doesn’t matter where a normal function is defined.
.call
, .apply
, .bind
, thisArg, and primitives
Another consequence of step 5 of OrdinaryCallBindThis, in conjunction with step 6.2 (6.b in the spec), is that a primitive this value is coerced to an object only in “sloppy” mode.
To examine this, let’s introduce another source for the this value: the three methods that override the this binding:4
Function.prototype.apply(thisArg, argArray)
-
Function.prototype.
{call
,bind
}(thisArg, ...args)
.bind
creates a bound function, whose this binding is set to thisArg and cannot change again. .call
and .apply
call the function immediately, with the this binding set to thisArg.
.call
and .apply
map directly to Call, using the specified thisArg. .bind
creates a bound function with BoundFunctionCreate. These have their own [[Call]] method which looks up the function object’s [[BoundThis]] internal slot.
Examples of setting a custom this value:
function f(){
console.log(this);
}
const myObj = {},
g = f.bind(myObj),
h = (m) => m();
// All of these log `myObj`.
g();
f.bind(myObj)();
f.call(myObj);
h(g);
For objects, this is the same in strict and non-strict mode.
Now, try to supply a primitive value:
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `String { "s" }`.
f.call(myString); // Logs `String { "s" }`.
In non-strict mode, primitives are coerced to their object-wrapped form. It’s the same kind of object you get when calling Object("s")
or new String("s")
. In strict mode, you can use primitives:
"use strict";
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `"s"`.
f.call(myString); // Logs `"s"`.
Libraries make use of these methods, e.g. jQuery sets the this
to the DOM element selected here:
$("button").click(function(){
console.log(this); // Logs the clicked button.
});
Constructors, classes, and new
When calling a function as a constructor using the new
operator, EvaluateNew calls Construct, which calls the [[Construct]] method. If the function is a base constructor (i.e. not a class extends
…{
…}
), it sets thisArgument to a new object created from the constructor’s prototype. Properties set on this
in the constructor will end up on the resulting instance object. this
is implicitly returned, unless you explicitly return your own non-primitive value.
A class
is a new way of creating constructor functions, introduced in ECMAScript 2015.
function Old(a){
this.p = a;
}
const o = new Old(1);
console.log(o); // Logs `Old { p: 1 }`.
class New{
constructor(a){
this.p = a;
}
}
const n = new New(1);
console.log(n); // Logs `New { p: 1 }`.
Class definitions are implicitly in strict mode:
class A{
m1(){
return this;
}
m2(){
const m1 = this.m1;
console.log(m1());
}
}
new A().m2(); // Logs `undefined`.
super
The exception to the behavior with new
is class extends
…{
…}
, as mentioned above. Derived classes do not immediately set their this value upon invocation; they only do so once the base class is reached through a series of super
calls (happens implicitly without an own constructor
). Using this
before calling super
is not allowed.
Calling super
calls the super constructor with the this value of the lexical scope (the function Environment Record) of the call. GetThisValue has a special rule for super
calls. It uses BindThisValue to set this
to that Environment Record.
class DerivedNew extends New{
constructor(a, a2){
// Using `this` before `super` results in a ReferenceError.
super(a);
this.p2 = a2;
}
}
const n2 = new DerivedNew(1, 2);
console.log(n2); // Logs `DerivedNew { p: 1, p2: 2 }`.
5. Evaluating class fields
Instance fields and static fields were introduced in ECMAScript 2022.
When a class
is evaluated, ClassDefinitionEvaluation is performed, modifying the running execution context. For each ClassElement:
- if a field is static, then
this
refers to the class itself, - if a field is not static, then
this
refers to the instance.
Private fields (e.g. #x
) and methods are added to a PrivateEnvironment.
Static blocks are currently a TC39 stage 3 proposal. Static blocks work the same as static fields and methods: this
inside them refers to the class itself.
Note that in methods and getters / setters, this
works just like in normal function properties.
class Demo{
a = this;
b(){
return this;
}
static c = this;
static d(){
return this;
}
// Getters, setters, private modifiers are also possible.
}
const demo = new Demo;
console.log(demo.a, demo.b()); // Both log `demo`.
console.log(Demo.c, Demo.d()); // Both log `Demo`.
1: (o.f)()
is equivalent to o.f()
; (f)()
is equivalent to f()
. This is explained in this 2ality article (archived). Particularly see how a ParenthesizedExpression is evaluated.
2: It must be a MemberExpression, must not be a property, must have a [[ReferencedName]] of exactly "eval", and must be the %eval% intrinsic object.
3: Whenever the specification says “Let ref be the result of evaluating X.”, then X is some expression that you need to find the evaluation steps for. For example, evaluating a MemberExpression or CallExpression is the result of one of these algorithms. Some of them result in a Reference Record.
4: There are also several other native and host methods that allow providing a this value, notably Array.prototype.map
, Array.prototype.forEach
, etc. that accept a thisArg as their second argument. Anyone can make their own methods to alter this
like (func, thisArg) => func.bind(thisArg)
, (func, thisArg) => func.call(thisArg)
, etc. As always, MDN offers great documentation.
Just for fun, test your understanding with some examples
For each code snippet, answer the question: “What is the value of this
at the marked line? Why?”.
To reveal the answers, click the gray boxes.
-
if(true){ console.log(this); // What is `this` here? }
globalThis
. The marked line is evaluated in the initial global execution context. -
const obj = {}; function myFun(){ return { // What is `this` here? "is obj": this === obj, "is globalThis": this === globalThis }; } obj.method = myFun; console.log(obj.method());
obj
. When calling a function as a property of an object, it is called with the this binding set to the base of the referenceobj.method
, i.e.obj
. -
const obj = { myMethod: function(){ return { // What is `this` here? "is obj": this === obj, "is globalThis": this === globalThis }; } }, myFun = obj.myMethod; console.log(myFun());
globalThis
. Since the function valuemyFun
/obj.myMethod
is not called off of an object, as a property, the this binding will beglobalThis
. This is different from Python, in which accessing a method (obj.myMethod
) creates a bound method object. -
const obj = { myFun: () => ({ // What is `this` here? "is obj": this === obj, "is globalThis": this === globalThis }) }; console.log(obj.myFun());
globalThis
. Arrow functions don’t create their own this binding. The lexical scope is the same as the initial global scope, sothis
isglobalThis
. -
function myFun(){ console.log(this); // What is `this` here? } const obj = { myMethod: function(){ eval("myFun()"); } }; obj.myMethod();
globalThis
. When evaluating the direct eval call,this
isobj
. However, in the eval code,myFun
is not called off of an object, so the this binding is set to the global object. -
function myFun() { // What is `this` here? return { "is obj": this === obj, "is globalThis": this === globalThis }; } const obj = {}; console.log(myFun.call(obj));
obj
. The linemyFun.call(obj);
is invoking the special built-in functionFunction.prototype.call
, which acceptsthisArg
as the first argument. -
class MyCls{ arrow = () => ({ // What is `this` here? "is MyCls": this === MyCls, "is globalThis": this === globalThis, "is instance": this instanceof MyCls }); } console.log(new MyCls().arrow());
It’s the instance of
MyCls
. Arrow functions don’t change the this binding, so it comes from lexical scope. Therefore, this is exactly the same as with the class fields mentioned above, likea = this;
. Try changing it tostatic arrow
. Do you get the result you expect?
Solution 2:
The this
keyword behaves differently in JavaScript compared to other languages. In Object Oriented languages, the this
keyword refers to the current instance of the class. In JavaScript the value of this
is determined by the invocation context of function (context.function()
) and where it is called.
1. When used in global context
When you use this
in global context, it is bound to global object (window
in browser)
document.write(this); //[object Window]
When you use this
inside a function defined in the global context, this
is still bound to global object since the function is actually made a method of global context.
function f1()
{
return this;
}
document.write(f1()); //[object Window]
Above f1
is made a method of global object. Thus we can also call it on window
object as follows:
function f()
{
return this;
}
document.write(window.f()); //[object Window]
2. When used inside object method
When you use this
keyword inside an object method, this
is bound to the "immediate" enclosing object.
var obj = {
name: "obj",
f: function () {
return this + ":" + this.name;
}
};
document.write(obj.f()); //[object Object]:obj
Above I have put the word immediate in double quotes. It is to make the point that if you nest the object inside another object, then this
is bound to the immediate parent.
var obj = {
name: "obj1",
nestedobj: {
name:"nestedobj",
f: function () {
return this + ":" + this.name;
}
}
}
document.write(obj.nestedobj.f()); //[object Object]:nestedobj
Even if you add function explicitly to the object as a method, it still follows above rules, that is this
still points to the immediate parent object.
var obj1 = {
name: "obj1",
}
function returnName() {
return this + ":" + this.name;
}
obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1
3. When invoking context-less function
When you use this
inside function that is invoked without any context (i.e. not on any object), it is bound to the global object (window
in browser)(even if the function is defined inside the object) .
var context = "global";
var obj = {
context: "object",
method: function () {
function f() {
var context = "function";
return this + ":" +this.context;
};
return f(); //invoked without context
}
};
document.write(obj.method()); //[object Window]:global
Trying it all with functions
We can try above points with functions too. However there are some differences.
- Above we added members to objects using object literal notation. We can add members to functions by using
this
. to specify them. - Object literal notation creates an instance of object which we can use immediately. With function we may need to first create its instance using
new
operator. - Also in an object literal approach, we can explicitly add members to already defined object using dot operator. This gets added to the specific instance only. However I have added variable to the function prototype so that it gets reflected in all instances of the function.
Below I tried out all the things that we did with Object and this
above, but by first creating function instead of directly writing an object.
/*********************************************************************
1. When you add variable to the function using this keyword, it
gets added to the function prototype, thus allowing all function
instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
this.name = "ObjDefinition";
this.getName = function(){
return this+":"+this.name;
}
}
obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition
/*********************************************************************
2. Members explicitly added to the function protorype also behave
as above: all function instances have their own copy of the
variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
return "v"+this.version; //see how this.version refers to the
//version variable added through
//prototype
}
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
3. Illustrating that the function variables added by both above
ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1
obj2.incrementVersion(); //incrementing version in obj2
//does not affect obj1 version
document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
4. `this` keyword refers to the immediate parent object. If you
nest the object through function prototype, then `this` inside
object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj',
getName1 : function(){
return this+":"+this.name;
}
};
document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj
/*********************************************************************
5. If the method is on an object's prototype chain, `this` refers
to the object the method was called on, as if the method was on
the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
//as its prototype
obj3.a = 999; //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
//calling obj3.fun() makes
//ProtoObj.fun() to access obj3.a as
//if fun() is defined on obj3
4. When used inside constructor function.
When the function is used as a constructor (that is when it is called with new
keyword), this
inside function body points to the new object being constructed.
var myname = "global context";
function SimpleFun()
{
this.myname = "simple function";
}
var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
// object being constructed thus adding any member
// created inside SimipleFun() using this.membername to the
// object being constructed
//2. And by default `new` makes function to return newly
// constructed object if no explicit return value is specified
document.write(obj1.myname); //simple function
5. When used inside function defined on prototype chain
If the method is on an object's prototype chain, this
inside such method refers to the object the method was called on, as if the method is defined on the object.
var ProtoObj = {
fun: function () {
return this.a;
}
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun()
//to be the method on its prototype chain
var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999
//Notice that fun() is defined on obj3's prototype but
//`this.a` inside fun() retrieves obj3.a
6. Inside call(), apply() and bind() functions
- All these methods are defined on
Function.prototype
. - These methods allows to write a function once and invoke it in different context. In other words, they allows to specify the value of
this
which will be used while the function is being executed. They also take any parameters to be passed to the original function when it is invoked. -
fun.apply(obj1 [, argsArray])
Setsobj1
as the value ofthis
insidefun()
and callsfun()
passing elements ofargsArray
as its arguments. -
fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- Setsobj1
as the value ofthis
insidefun()
and callsfun()
passingarg1, arg2, arg3, ...
as its arguments. -
fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- Returns the reference to the functionfun
withthis
inside fun bound toobj1
and parameters offun
bound to the parameters specifiedarg1, arg2, arg3,...
. - By now the difference between
apply
,call
andbind
must have become apparent.apply
allows to specify the arguments to function as array-like object i.e. an object with a numericlength
property and corresponding non-negative integer properties. Whereascall
allows to specify the arguments to the function directly. Bothapply
andcall
immediately invokes the function in the specified context and with the specified arguments. On the other hand,bind
simply returns the function bound to the specifiedthis
value and the arguments. We can capture the reference to this returned function by assigning it to a variable and later we can call it any time.
function add(inc1, inc2)
{
return this.a + inc1 + inc2;
}
var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
//above add.call(o,5,6) sets `this` inside
//add() to `o` and calls add() resulting:
// this.a + inc1 + inc2 =
// `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
// `o.a` i.e. 4 + 5 + 6 = 15
var g = add.bind(o, 5, 6); //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />"); //15
var h = add.bind(o, 5); //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
// 4 + 5 + 6 = 15
document.write(h() + "<br />"); //NaN
//no parameter is passed to h()
//thus inc2 inside add() is `undefined`
//4 + 5 + undefined = NaN</code>
7. this
inside event handlers
- When you assign function directly to event handlers of an element, use of
this
directly inside event handling function refers to the corresponding element. Such direct function assignment can be done usingaddeventListener
method or through the traditional event registration methods likeonclick
. - Similarly, when you use
this
directly inside the event property (like<button onclick="...this..." >
) of the element, it refers to the element. - However use of
this
indirectly through the other function called inside the event handling function or event property resolves to the global objectwindow
. - The same above behavior is achieved when we attach the function to the event handler using Microsoft's Event Registration model method
attachEvent
. Instead of assigning the function to the event handler (and the thus making the function method of the element), it calls the function on the event (effectively calling it in global context).
I recommend to better try this in JSFiddle.
<script>
function clickedMe() {
alert(this + " : " + this.tagName + " : " + this.id);
}
document.getElementById("button1").addEventListener("click", clickedMe, false);
document.getElementById("button2").onclick = clickedMe;
document.getElementById("button5").attachEvent('onclick', clickedMe);
</script>
<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>
<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />
<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />
IE only: <button id="button5">click() "attached" using attachEvent() </button>
8. this
in ES6 arrow function
In an arrow function, this
will behave like common variables: it will be inherited from its lexical scope. The function's this
, where the arrow function is defined, will be the arrow function's this
.
So, that's the same behavior as:
(function(){}).bind(this)
See the following code:
const globalArrowFunction = () => {
return this;
};
console.log(globalArrowFunction()); //window
const contextObject = {
method1: () => {return this},
method2: function(){
return () => {return this};
}
};
console.log(contextObject.method1()); //window
const contextLessFunction = contextObject.method1;
console.log(contextLessFunction()); //window
console.log(contextObject.method2()()) //contextObject
const innerArrowFunction = contextObject.method2();
console.log(innerArrowFunction()); //contextObject