Explain how `this` works in JavaScript
TL;DR
There's no simple explanation for this
; it is one of the most confusing concepts in JavaScript because it's behavior differs from many other programming languages. The one-liner explanation of the this
keyword is that it is a dynamic reference to the context in which a function is executed.
A longer explanation follows is that this
follows these rules:
- If the
new
keyword is used when calling the function, meaning the function was used as a function constructor, thethis
inside the function is the newly-created object instance. - If
this
is used in aclass
constructor
, thethis
inside theconstructor
is the newly-created object instance. - If
apply()
,call()
, orbind()
is used to call/create a function,this
inside the function is the object that is passed in as the argument. - If a function is called as a method (e.g.
obj.method()
) —this
is the object that the function is a property of. - If a function is invoked as a free function invocation, meaning it was invoked without any of the conditions present above,
this
is the global object. In the browser, the global object is thewindow
object. If in strict mode ('use strict';
),this
will beundefined
instead of the global object. - If multiple of the above rules apply, the rule that is higher wins and will set the
this
value. - If the function is an ES2015 arrow function, it ignores all the rules above and receives the
this
value of its surrounding scope at the time it is created.
For an in-depth explanation, do check out Arnav Aggrawal's article on Medium.
this
keyword
In JavaScript, this
is a keyword that refers to the current execution context of a function or script. It's a fundamental concept in JavaScript, and understanding how this
works is crucial for building robust and maintainable applications.
Used globally
In the global scope, this
refers to the global object, which is the window
object in a web browser or the global
object in a Node.js environment.
console.log(this); // In a browser, this will log the window object (for non-strict mode).
Within a regular function call
When a function is called in the global context or as a standalone function, this
refers to the global object (in non-strict mode) or undefined
(in strict mode).
function showThis() {console.log(this);}showThis(); // In non-strict mode: Window (global object). In strict mode: undefined.
Within a method call
When a function is called as a method of an object, this
refers to the object that the method is called on.
const obj = {name: 'John',showThis: function () {console.log(this);},};obj.showThis(); // { name: 'John', showThis: ƒ }
Note that if you do the following, it is as good as a regular function call and not a method call. this
has lost its context and no longer points to obj
.
const obj = {name: 'John',showThis: function () {console.log(this);},};const showThisStandalone = obj.showThis;showThisStandalone(); // In non-strict mode: Window (global object). In strict mode: undefined.
Within a function constructor
When a function is used as a constructor (called with the new
keyword), this
refers to the newly-created instance. In the following example, this
refers to the Person
object being created, and the name
property is set on that object.
function Person(name) {this.name = name;}const person = new Person('John');console.log(person.name); // "John"
Within class constructor and methods
In ES2015 classes, this
behaves as it does in object methods. It refers to the instance of the class.
class Person {constructor(name) {this.name = name;}showThis() {console.log(this);}}const person = new Person('John');person.showThis(); // Person {name: 'John'}const showThisStandalone = person.showThis;showThisStandalone(); // `undefined` because all parts of a class' body are strict mode.
Explicitly binding this
You can use bind()
, call()
, or apply()
to explicitly set the value of this
for a function.
Using the call()
and apply()
methods allow you to explicitly set the value of this
when calling the function.
function showThis() {console.log(this);}const obj = { name: 'John' };showThis.call(obj); // { name: 'John' }showThis.apply(obj); // { name: 'John' }
The bind()
method creates a new function with this
bound to the specified value.
function showThis() {console.log(this);}const obj = { name: 'John' };const boundFunc = showThis.bind(obj);boundFunc(); // { name: 'John' }
Within arrow functions
Arrow functions do not have their own this
context. Instead, the this
is lexically scoped, which means it inherits the this
value from its surrounding scope at the time they are defined.
In this example, this
refers to the global object (window or global), because the arrow function is not bound to the person
object.
const person = {name: 'John',sayHello: () => {console.log(`Hello, my name is ${this.name}!`);},};person.sayHello(); // "Hello, my name is undefined!"
In the following example, the this
in the arrow function will be the this
value of its enclosing context, so it depends on how showThis()
is called.
const obj = {name: 'John',showThis: function () {const arrowFunc = () => {console.log(this);};arrowFunc();},};obj.showThis(); // { name: 'John', showThis: ƒ }const showThisStandalone = obj.showThis;showThisStandalone(); // In non-strict mode: Window (global object). In strict mode: undefined.
Therefore, the this
value in arrow functions cannot be set by bind()
, apply()
or call()
methods, nor does it point to the current object in object methods.
const obj = {name: 'Alice',regularFunction: function () {console.log('Regular function:', this.name);},arrowFunction: () => {console.log('Arrow function:', this.name);},};const anotherObj = {name: 'Bob',};// Using call/apply/bind with a regular functionobj.regularFunction.call(anotherObj); // Regular function: Bobobj.regularFunction.apply(anotherObj); // Regular function: Bobconst boundRegularFunction = obj.regularFunction.bind(anotherObj);boundRegularFunction(); // Regular function: Bob// Using call/apply/bind with an arrow function, `this` refers to the global scope and cannot be modified.obj.arrowFunction.call(anotherObj); // Arrow function: window/undefined (depending if strict mode)obj.arrowFunction.apply(anotherObj); // Arrow function: window/undefined (depending if strict mode)const boundArrowFunction = obj.arrowFunction.bind(anotherObj);boundArrowFunction(); // Arrow function: window/undefined (depending if strict mode)
Within event handlers
When a function is called as a DOM event handler, this
refers to the element that triggered the event. In this example, this
refers to the <button>
element that was clicked.
<button id="my-button" onclick="console.log(this)">Click me</button><!-- Logs the button element -->
When setting an event handler using JavaScript, this also refers to the element that received the event.
document.getElementById('my-button').addEventListener('click', function () {console.log(this); // Logs the button element});
As mentioned above, ES2015 introduces arrow functions which uses the enclosing lexical scope. This is usually convenient, but does prevent the caller from defining the this
context via .call
/.apply
/.bind
. One of the consequences is that DOM event handlers will not properly bind this
in your event handler functions if you define the callback parameters to .addEventListener()
using arrow functions.
document.getElementById('my-button').addEventListener('click', () => {console.log(this); // Window / undefined (depending on whether strict mode) instead of the button element.});
In summary, this
in JavaScript refers to the current execution context of a function or script, and its value can change depending on the context in which it is used. Understanding how this
works is essential for building robust and maintainable JavaScript applications.