In JavaScript, the special variable this
is relatively complicated, because it is available everywhere, not just in object-oriented settings.
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 ECMAScript 2015 introduced arrow functions whose this
is lexically scoped (it is set to the this
value of the enclosing execution context).
Global context Edit
In the global execution context (outside of any function), this
refers to the global object, whether in strict mode or not.
console.log(this.document === document); // true // In web browsers, the window object is also the global object: console.log(this === window); // true this.a = 37; console.log(window.a); // 37
Function context Edit
Inside a function, the value of this
depends on how the function is called.
Simple call
function f1(){ return this; } f1() === window; // global object
In this case, the value of this
is not set by the call. Since the code is not in strict mode, the value of this
must always be an object so it defaults to the global object.
function f2(){ "use strict"; // see strict mode return this; } f2() === undefined;
In strict mode, the value of this
remains at whatever it's set to when entering the execution context. If it's not defined, it remains undefined. It can also be set to any value, such as null
or 42
or "I am not this"
.
this
should be undefined
, because f2
was called directly and not as a method or property of an object (e.g. window.f2()
). This feature wasn't implemented in some browsers when they first started to support strict mode. As a result, they incorrectly returned the window
object.Arrow functions
In arrow functions, this
is set lexically, i.e. it's set to the value of the enclosing execution context's this
. In global code, it will be set to the global object:
var globalObject = this; var foo = (() => this); console. log( foo() === globalObject); // true
It doesn't matter how foo
is called, its this
will stay as the global object. This also holds if it's called as a method of an object (which would usually set its this
to the object), with call
or apply
or bind
is used:
// Call as a method of an object var obj = {foo: foo}; console. log(obj. foo() === globalObject); // true // Attempt to set this using call console. log(foo. call(obj) === globalObject); // true // Attempt to set this using bind foo = foo. bind(obj); console. log( foo() === globalObject); // true
No matter what, foo
's this
is set to what it was when it was created (in the example above, the global object). The same applies for arrow functions created inside other functions: their this
is set to that of the outer execution context.
// Create obj with a method bar that returns a function that // returns its this. The returned function is created as // an arrow function, so its this is permanently bound to the // this of its enclosing function. The value of bar can be set // in the call, which in turn sets the value of the // returned function. var obj = { bar : function() { var x = (() => this); return x; } }; // Call bar as a method of obj, setting its this to obj // Assign a reference to the returned function to fn var fn = obj. bar(); // Call fn without setting this, would normally default // to the global object or undefined in strict mode console. log( fn() === obj); // true
In the above, the function(call it anonymous function A) assigned to obj.bar
returns another function(call it anonymous function B) that is created as an arrow function. As a result, function B's this
is permanently set to the this
of obj.bar
(function A)when called. When the returned function(function B) is called, its this
will always be what it was set to initially. In the above code example, function B's this
is set to function A's this
which is obj, so it remains set to obj
even when called in a manner that would normally set its this
to undefined
or the global object (or any other method as in the previous example in the global execution context).
As an object method
When a function is called as a method of an object, its this
is set to the object the method is called on.
In the following example, when o.f()
is invoked, inside the function this
is bound to the o
object.
var o = { prop: 37, f: function() { return this.prop; } }; console. log(o. f()); // logs 37
Note that this behavior is not at all affected by how or where the function was defined. In the previous example, we defined the function inline as the f
member during the definition of o
. However, we could have just as easily defined the function first and later attached it to o.f
. Doing so results in the same behavior:
var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console. log(o. f()); // logs 37
This demonstrates that it matters only that the function was invoked from the f
member of o
.
Similarly, the this
binding is only affected by the most immediate member reference. In the following example, when we invoke the function, we call it as a method g
of the object o.b
. This time during execution, this
inside the function will refer to o.b
. The fact that the object is itself a member of o
has no consequence; the most immediate reference is all that matters.
o.b = {g: independent, prop: 42}; console. log(o.b. g()); // logs 42
this
on the object's prototype chain
The same notion holds true for methods defined somewhere on the object's prototype chain. 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 o = {f:function(){ return this.a + this.b; }}; var p = Object. create(o); p.a = 1; p.b = 4; console. log(p. f()); // 5
In this example, the object assigned to the variable p
doesn't have its own f
property, it inherits it from its prototype. But it doesn't matter that the lookup for f
eventually finds a member with that name on o
; the lookup began as a reference to p.f
, so this
inside the function takes the value of the object referred to as p
. That is, since f
is called as a method of p
, its this
refers to p
. This is an interesting feature of JavaScript's prototype inheritance.
this
with a getter or setter
Again, the same notion holds true when a function is invoked from a getter or a setter. A function used as getter or setter has its this
bound to the object from which the property is being set or gotten.
function sum(){ return this.a + this.b + this.c; } var o = { a: 1, b: 2, c: 3, get average(){ return (this.a + this.b + this.c) / 3; } }; Object. defineProperty(o, 'sum', { get: sum, enumerable:true, configurable:true}); console. log(o.average, o.sum); // logs 2, 6
As a constructor
When a function is used as a constructor (with the new
keyword), its this
is bound to the new object being constructed.
While the default for a constructor is to return the object referenced by this
, it can instead return some other object (if the return value isn't an object, then the this
object is returned).
/* * Constructors work like this: * * function MyConstructor(){ * // Actual function body code goes here. * // Create properties on |this| as * // desired by assigning to them. E.g., * this.fum = "nom"; * // et cetera... * * // If the function has a return statement that * // returns an object, that object will be the * // result of the |new| expression. Otherwise, * // the result of the expression is the object * // currently bound to |this| * // (i.e., the common case most usually seen). * } */ function C(){ this.a = 37; } var o = new C(); console. log(o.a); // logs 37 function C2(){ this.a = 37; return {a:38}; } o = new C2(); console. log(o.a); // logs 38
In the last example (C2
), because an object was returned during construction, the new object that this
was bound to simply gets discarded. (This essentially makes the statement "this.a = 37;
" dead code. It's not exactly dead, because it gets executed, but it can be eliminated with no outside effects.)
call
and apply
Where a function uses the this
keyword in its body, its value can be bound to a particular object in the call using the call
or apply
methods which all functions inherit from Function.prototype
.
function add(c, d){ return this.a + this.b + c + d; } var o = {a:1, b:3}; // The first parameter is the object to use as // 'this', subsequent parameters are passed as // arguments in the function call add. call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 // The first parameter is the object to use as // 'this', the second is an array whose // members are used as the arguments in the function call add. apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
Note that with call
and apply
, if the value passed as this
is not an object, an attempt will be made to convert it to an object using the internal ToObject
operation. So if the value passed is a primitive like 7
or 'foo'
, it will be converted to an Object using the related constructor, so the primitive number 7
is converted to an object as if by new Number(7)
and the string 'foo'
to an object as if by new String('foo')
, e.g.
function bar() { console. log(Object.prototype.toString. call(this)); } bar. call(7); // [object Number]
The bind
method
ECMAScript 5 introduced Function.prototype.bind
. Calling f.bind(someObject)
creates a new function with the same body and scope as f
, but where this
occurs in the original function, in the new function it is permanently bound to the first argument of bind
, regardless of how the function is being used.
function f(){ return this.a; } var g = f. bind({a:"azerty"}); console. log( g()); // azerty var o = {a:37, f:f, g:g}; console. log(o. f(), o. g()); // 37, azerty
As a DOM event handler
When a function is used as an event handler, its this
is set to the element the event fired from (some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener
).
// When called as a listener, turns the related element blue function bluify(e){ // Always true console. log(this === e.currentTarget); // true when currentTarget and target are the same object console. log(this === e.target); this.style.backgroundColor = '#A5D9F3'; } // Get a list of every element in the document var elements = document. getElementsByTagName('*'); // Add bluify as a click listener so when the // element is clicked on, it turns blue for(var i=0 ; i<elements.length ; i++){ elements[i]. addEventListener('click', bluify, false); }
In an in–line event handler
When code is called from an in–line on-event handler, its this
is set to the DOM element on which the listener is placed:
<button onclick="alert(this.tagName.toLowerCase());"> Show this </button>
The above alert shows button
. Note however that only the outer code has its this
set this way:
<button onclick="alert((function(){return this})());"> Show inner this </button>
In this case, the inner function's this
isn't set so it returns the global/window object (i.e. the default object in non–strict mode where this
isn't set by the call).
this
in eval()
eval()
can be called either directly (via a real function call) or indirectly (via some other means). The details are explained here.
If eval()
is called indirectly, this
refers to the global objec:
> (0,eval)('this === window') true
Otherwise, if eval()
is called directly, this
remains the same as in the surroundings of eval()
. For example:
// Real functions function sloppyFunc() { console.log(eval('this') === window); // true } sloppyFunc(); function strictFunc() { 'use strict'; console.log(eval('this') === undefined); // true } strictFunc(); // Constructors var savedThis; function Constr() { savedThis = eval('this'); } var inst = new Constr(); console.log(savedThis === inst); // true // Methods var obj = { method: function () { console.log(eval('this') === obj); // true } } obj.method();
this
-related pitfalls
There are three this
-related pitfalls that you should be aware of. Note that in each case, strict mode makes things safer, because this
is undefined
in real functions and you get warnings when things go wrong.
Pitfall: forgetting new
If you invoke a constructor and forget the new
operator, you are accidently using it as a real function. Hence, this
does not have the correct value. In sloppy mode, this
is window
and you’ll create global variables:
function Point(x, y) { this.x = x; this.y = y; } var p = Point(7, 5); // we forgot new! console.log(p === undefined); // true // Global variables have been created: console.log(x); // 7 console.log(y); // 5
Thankfully, you get a warning in strict mode (this === undefined
):
function Point(x, y) { 'use strict'; this.x = x; this.y = y; } var p = Point(7, 5); // TypeError: Cannot set property 'x' of undefined
Pitfall: extracting methods improperly
If you retrieve the value of a method (instead of invoking it), you turn the method into a function. Calling the value results in a function call, not a method call. This kind of extraction can happen when you pass a method as an argument for a function or method call. Real-world examples include setTimeout()
and registering event handlers. I’ll use the function callIt()
to simulate this use case synchronously:
/** Similar to setTimeout() and setImmediate() */ function callIt(func) { func(); }
If you call a sloppy-mode method as a function, this
refers to the global object and global variables will be created:
var counter = { count: 0, // Sloppy-mode method inc: function () { this.count++; } } callIt(counter.inc); // Didn’t work: console.log(counter.count); // 0 // Instead, a global variable has been created // (NaN is result of applying ++ to undefined): console.log(count); // NaN
If you call a strict-mode method as a function, this
is undefined
. Things don’t work, either. But at least you get a warning:
var counter = { count: 0, // Strict-mode method inc: function () { 'use strict'; this.count++; } } callIt(counter.inc); // TypeError: Cannot read property 'count' of undefined console.log(counter.count);
The fix is to use bind()
:
var counter = { count: 0, inc: function () { this.count++; } } callIt(counter.inc.bind(counter)); // It worked! console.log(counter.count); // 1
bind()
created a new function that always receives a this
whose value is counter
.
Pitfall: shadowing this
When you use a real function inside a method, it is easy to forget that the former has its own this
(even though it has no need for it). Therefore, you can’t refer from the former to the method’s this
, because it is shadowed. Let’s look at an example where things go wrong:
var obj = { name: 'Jane', friends: [ 'Tarzan', 'Cheeta' ], loop: function () { 'use strict'; this.friends.forEach( function (friend) { console.log(this.name+' knows '+friend); } ); } }; obj.loop(); // TypeError: Cannot read property 'name' of undefined
In the previous example, this.name
fails, because the function’s this
is undefined
, it is not the same as the this
of the method loop()
. There are three ways to fix … this.
Fix 1: that = this
. Assign this
to a variable that isn’t shadowed (another popular name is self
) and use that one.
loop: function () { 'use strict'; var that = this; this.friends.forEach(function (friend) { console.log(that.name+' knows '+friend); }); }
Fix 2: bind()
. Use bind()
to create a function whose this
always has the correct value (the method’s this
in the following example).
loop: function () { 'use strict'; var that = this; this.friends.forEach(function (friend) { console.log(that.name+' knows '+friend); }); }
Fix 3: forEach
’s second parameter. This method has a second parameter whose value is passed to the callback as this
.
loop: function () { 'use strict'; this.friends.forEach(function (friend) { console.log(this.name+' knows '+friend); }, this); }
Best practices
Conceptually, I think of real functions as not having their own this
and think of the aforementioned fixes as keeping up that illusion. ECMAScript 6 supports this approach via arrow functions – functions without their own this
. Inside such functions, you can freely use this
, because there is no shadowing:
loop: function () { 'use strict'; // The parameter of forEach() is an arrow function this.friends.forEach(friend => { // `this` is loop’s `this` console.log(this.name+' knows '+friend); }); }
I don’t like APIs that use this
as an additional parameter of real functions:
beforeEach(function () { this.addMatchers({ toBeInRange: function (start, end) { ... } }); });
Turning such an implicit parameter into an explicit one makes things more obvious and is compatible with arrow functions.
beforeEach(api => { api.addMatchers({ toBeInRange(start, end) { ... } }); });