This content originally appeared on Telerik Blogs and was authored by Ifeoma Imoh
In this article, we will look at what the this
keyword is and the four rules that determine its behavior in JavaScript.
Compared to other languages, the this
keyword acts differently in JavaScript. It is one of the most common JavaScript keywords. As confusing as it can be, it is a fundamental concept because it allows for flexibility, reusing a function in
multiple contexts.
What Is the ‘This’?
When you invoke a function in JavaScript, a new execution context is created and added to the call stack. The execution context contains a this
reference or a this
binding that will be used throughout the function’s execution.
What this
references is entirely determined by the call site (the location where a function is called, not where it is declared).
So, for every function invocation, there is a special identifier called this
, but this
isn’t concerned with where a function is defined; instead, it is interested in how it is called.
The this
keyword can be used to invoke functions in different contexts, and depending on the context, this
might mean something entirely different every time. The this
keyword never points at the function itself.
It always points to a context object that can be identified by looking at the call site of a function. How then can we determine what the value of this
points at for any given function?
4 Rules for Binding ‘This’
In JavaScript, there are four different ways to invoke a function, and each one of them provides different context objects for the this
keyword to point at when executing a function. The four rules for binding this
are determined
by how we invoke functions in JavaScript. Understanding these four ways can help us understand how the this
keyword gets bound.
Implicit Binding
When we invoke a function in the context of an object that owns or contains the function’s this
, it will point to that object.
Consider the following example:
function sayHello() {
console.log(this.greet)
}
var greet1 = {greet: 'Hello', sayHello: sayHello}
var greet2 = {greet: 'hi', sayHello: sayHello}
greet1.sayHello() // Hello
greet2.sayHello() // Hi
In the snippet above, we’re borrowing a reference to the sayHello
function and setting its reference directly on the objects greet1
and greet2
. In so doing, we can implicitly call sayHello
in the
context of any of the objects. JavaScript will simply call the sayHello
function and set its this
keyword equal to the context object used to invoke the function.
It decides what the this
keyword will point at based on what object is used to invoke the sayHello
function. Looking at both call sites in the example above, we can tell that the this
keyword will be equal to the
object in front of the function call. So greet2.sayHello()
says invoke the function sayHello
with its this
keyword pointing at the greet2
object.
Consider this object containing a function:
const myObj = {
greet: 'Hello',
func: function sayHello() {
console.log(this.greet)
},
}
myObj.func() // Hello
We have an object that contains a property that holds a function. This property is also known as a method. Whenever this method is called, its this
keyword will be bound to its immediate enclosing object—myObj
. This is
true for both strict and non-strict modes.
Explicit Binding
Functions in JavaScript are considered first-class objects, which means that they can be stored in variables, passed around, returned from other functions, and even hold their properties. All functions descend from the built-in Function
object
and they inherit methods defined on the Function.prototype object.
We can use two of its methods, call()
and apply()
, to invoke a function in different contexts or with different context objects. These methods execute the function pointing to the provided object context as the value of this
.
Consider the following example:
function sayHello() {
console.log(this.greet)
}
var greet1 = {
greet: 'Hello',
}
sayHello.call(greet1) // Hello
sayHello.apply(greet1) // Hello
In the example above, we’re invoking the sayHello
function using the call()
and apply()
methods and they both accept an object—greet1
as their first argument. Explicitly invoking the sayHello
function with these methods will force the function to use the object greet1
for its this
binding in both cases.
Both call()
and apply()
behave identically and will set greet1
as the value of this
inside the sayHello
function. Still, the difference between the two methods is how they handle additional
parameters, but we won’t cover that in this article.
In non-strict mode, if you pass a primitive value as the “this” binding, the primitive value will be coerced to its object-wrapped form.
Variation of Explicit Binding
Unfortunately, there is a possibility of losing the intended this
binding or having it set to the global object when passing functions around or providing a callback to another function. The function method bind()
is a utility
built into JavaScript, and it was added in ES5 to set the value of a function’s this
regardless of how the function is called.
Consider the following example:
var greet = 'Hi'
function sayHello(cb) {
cb()
}
var obj = {
greet: 'Hello',
greeting: function () {
console.log(this.greet)
},
}
sayHello(obj.greeting) // Hi
sayHello(obj.greeting.bind(obj)) // Hello
In the example above, when we pass obj.greeting
as a callback to the function sayHello()
, its this
keyword loses its this
binding to obj
and points to the global object. this.greet
is not referring to obj
—instead it is referring to the global object. This can be solved by using the bind()
method to set the value of this
to obj
.
bind()
doesn’t invoke the function immediately. It returns a function with “this” bound to the object it receives as its first argument.
The ‘New’ Keyword
Invoking a function with the new
keyword is referred to as a constructor call. When a function is called with the new
keyword in front of it, it does four things:
- It creates a brand new empty object.
- The newly created object is linked to the function’s prototype object.
- The function is invoked with its
this
keyword pointing to the new object - If the function doesn’t return an object, it implies a return
this
. With thethis
keyword already pointing to the newly created object, the newly created object is returned automatically.
Consider the following:
function sayHello(greet) {
this.greet = greet
}
let me = new sayHello('Hello Ifeoma')
console.log(me.greet) // Hello Ifeoma
In the example above, calling the function sayHello
with the new
keyword would immediately create a new object that is an instance of the function sayHello
. The this
binding inside the function body
points to the new object that was created and assigned to the variable me
.
Default Binding
When you use this
inside a function that is invoked without setting the call to any context object, by default, this will point to the global object, which is the window in a browser. The default binding is applied when a function is called
the regular way (e.g., sayHello()
).
Consider the following:
var greet = 'Hello'
function sayHello() {
// not in strict mode
console.log(this)
console.log(this.greet)
}
sayHello() // Window
// Hello
In the example above, when we call our sayHello
function, this.greet
resolves to our global variable greet
because variables defined in the global scope are global object properties. We called sayHello
without setting any context object, so the default binding applies here if the function is not in strict mode.
var greet = 'Hello'
function sayHello() {
// not in strict mode
'use strict'
console.log(this)
console.log(this.greet)
}
sayHello() // undefined
// TypeError: Cannot read property 'greet' of undefined
In the example above, the content of our function is running in strict mode, so the global object is not eligible for the default binding in this case. The value of this
, in this case, is set to undefined.
Arrow Functions
Arrow functions, by default, do not define a this
keyword, meaning that they do not have their own this
binding. If you define a this
keyword within an arrow function, it’s no different from declaring a regular
variable in JavaScript. It will lexically resolve to some enclosing scope that does define a this
keyword or the global scope.
To explain how this
works with regard to the arrow functions, consider the following:
let obj = {
greet: 'hello',
func: function sayHello() {
return () => {
console.log(this.greet)
}
},
}
let a = obj.func()
a() // Hello
In the example above, the this
keyword in the arrow function inside our sayHello
function refers to the value of this
in the arrow function environment (where it was defined). In our case, it’ll look up the
scope chain to see if its parent scope has a this
keyword; since the enclosing scope containing the sayHello
function is an object, the value of this
in the arrow function points to the object.
Order of Precedence
What is the order of precedence if more than one rule matches a call site?
- If the function was called with the
new
keyword,this
would point to the newly created object. - If it was called with any of these—
call()
,apply()
orbind()
—use the specified context object as the value ofthis
. - If it was called with a context object, the value of
this
would point to that object. - Finally, default to the global object if any of the rules above don’t match and the function isn’t running in strict mode.
This content originally appeared on Telerik Blogs and was authored by Ifeoma Imoh
Ifeoma Imoh | Sciencx (2022-10-28T08:11:00+00:00) What Is ‘This’ in JavaScript?. Retrieved from https://www.scien.cx/2022/10/28/what-is-this-in-javascript-4/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.