The JavaScript this Keyword Get Certified
this is one of the most confusing parts of
JavaScript, because its value is not fixed — it depends
entirely on how a function is called, not
where it is defined.
In this lesson, you will learn how this behaves in
regular functions, object methods, classes, and arrow
functions, how it is easy to accidentally lose, and how to fix
it with bind(), call(),
apply(), or arrow functions.
this in a Plain Function Call
When a regular function is called on its own, without being
attached to an object, this refers to the global
object (in non-strict mode) rather than to anything specific.
What this code does: Shows the default value of
this when a regular function is called without an
owner object (global / undefined in strict mode).
function showThis() {
console.log(typeof this);
console.log(this === self);
}
showThis();
function showThis() — a regular function, not a method on an object.console.log(typeof this) — in non-strict mode this is usually the global object (object); in strict mode it is undefined.console.log(this === self) — self is the global object in browsers/workers; true when this is the global object.}showThis() — because there is no object before the call, this is not set to a useful object. This is the classic “lost this” starting point.this Inside an Object Method
When a function is called as a method — that is,
object.method() — this refers to the
object the method was called on.
What this code does: Demonstrates that inside an object method
this refers to the object the method was called on.
const person = {
name: "Michael",
greet() {
return "Hello, " + this.name;
}
};
console.log(person.greet());
const person = {name: "Michael"greet() { return "Hello, " + this.name; } — when called as a method, this is the object before the dot.}console.log(person.greet()) — person is the receiver, so this.name is “Michael”.Losing this by Extracting a Method
The value of this depends on the call, not on where
the function was originally written. If you copy a method out
of an object and call it separately, it no longer knows which
object it “belongs” to.
What this code does: Extracts a method from an object and calls
it as a free function, causing this to be lost.
const person = {
name: "Michael",
greet() {
return "Hello, " + this.name;
}
};
const greetFn = person.greet;
console.log(greetFn());
const greetFn = person.greet — copies the function reference only; the connection to person is lost.console.log(greetFn()) — now this is undefined (strict) or global (non-strict), so this.name is wrong or throws. This is the most common this bug.
Called this way, this no longer refers to
person, so this.name is
undefined instead of "Michael".
Fixing this with bind()
bind() returns a new function permanently bound to
a specific this value, no matter how that new
function is later called.
What this code does: Fixes the lost this by using
.bind() so the extracted function permanently keeps
the original object.
const person = {
name: "Michael",
greet() {
return "Hello, " + this.name;
}
};
const boundGreet = person.greet.bind(person);
console.log(boundGreet());
const boundGreet = person.greet.bind(person) — returns a new function whose this is always person, no matter how it is later called.console.log(boundGreet()) — works even though there is no object before the call. Use bind when you must pass a method as a standalone callback.Setting this with call() and apply()
call() and apply() invoke a function
immediately with a specific this value.
call() takes arguments individually;
apply() takes them as an array.
What this code does: Uses call() and
apply() to invoke a function with an explicit
this value.
function introduce(greeting) {
return greeting + ", " + this.name;
}
const person = { name: "Michael" };
console.log(introduce.call(person, "Hi"));
console.log(introduce.apply(person, ["Hello"]));
function introduce(greeting) { return greeting + ", " + this.name; } — expects this to have a name property.const person = { name: "Michael" }introduce.call(person, "Hi") — this becomes person; arguments are listed individually.introduce.apply(person, ["Hello"]) — same this, but arguments come as an array. call/apply invoke immediately; bind returns a new function for later.Arrow Functions and Lexical this
Arrow functions do not have their own this. Instead,
they use the this value from the surrounding
(enclosing) scope where they were defined.
What this code does: Shows that an arrow function inside a method
inherits this from the surrounding method, not from
how it is later called.
const person = {
name: "Michael",
greet: function () {
const arrow = () => {
return "Hello, " + this.name;
};
return arrow();
}
};
console.log(person.greet());
const person = {name: "Michael"greet: function () { — this will be person when called as person.greet().const arrow = () => { return "Hello, " + this.name; } — arrows do not create their own this; they inherit this from the enclosing greet function, which is person.return arrow()} }console.log(person.greet()) — the arrow correctly sees this.name because it closed over the method’s this.
Because arrow is defined inside greet,
it inherits this from greet, which is
person.
Pitfall: Arrow Functions as Object Methods
Because arrow functions do not get their own this,
defining an object’s method directly as an arrow function
usually does not work the way you expect.
What this code does: Uses an arrow function as an object method,
which fails because arrows do not get their own this
from the object.
const person = {
name: "Michael",
greet: () => {
return "Hello, " + this.name;
}
};
console.log(person.greet());
const person = {name: "Michael"greet: () => { return "Hello, " + this.name; } — arrows take this from the surrounding scope (here, the module/global), not from the object they sit on. So this is not person.}console.log(person.greet()) — this.name is undefined (or a global name). Never define object methods as arrow functions if they need this.
Here, this refers to whatever this was
outside the object literal, not to person, so
this.name is undefined.
this Inside a Class
Inside a class method, this refers to the instance
the method was called on, just like a regular object method.
What this code does: Inside a class method, this
correctly refers to the instance the method was called on.
class Person {
constructor(name) {
this.name = name;
}
greet() {
return "Hello, " + this.name;
}
}
const michael = new Person("Michael");
console.log(michael.greet());
class Personconstructor(name) { this.name = name; } — this is the newly created instance.greet() { return "Hello, " + this.name; } — when called as michael.greet(), this is michael.}const michael = new Person("Michael")console.log(michael.greet()) — inside a class, this behaves the same as a method on a plain object: it is the receiver of the call.Fixing It with an Arrow Function
Using an arrow function for the callback keeps
this pointing at the instance, because arrow
functions inherit this from where they are defined.
What this code does:
Uses an arrow function inside setTimeout so
this still refers to the class instance.
class Timer {
constructor(label) {
this.label = label;
}
start() {
console.log("Timer started →", this.label);
setTimeout(() => {
console.log("After delay → Label is still:", this.label);
}, 30);
}
}
const t = new Timer("Countdown");
t.start();
console.log("start() called (watch for delayed message)");
class Timerconstructor(label) { this.label = label; }start() {console.log("Timer started →", this.label)setTimeout(() => { console.log("After delay → Label is still:", this.label); }, 30) — the arrow inherits this from start(), so this is still the Timer instance after the delay. A regular function would have lost it.}}const t = new Timer("Countdown"); t.start();console.log("start() called (watch for delayed message)") — the delayed log still shows the correct label because of the arrow.this Binding Rules
| How the Function Is Called | Value of this |
|---|---|
| Plain function call: fn() | Global object (non-strict) or undefined (strict) |
| Method call: obj.method() | The object before the dot (obj) |
| Constructor call: new Fn() | The newly created instance |
| fn.call(obj) / fn.apply(obj) | Whatever object is passed in |
| fn.bind(obj) | Permanently bound to obj |
| Arrow function | Inherited from the enclosing scope |
Common Mistakes with this
- Extracting a method from an object and calling it separately, losing its connection to that object.
- Passing a regular function as a callback (like to
setTimeout) and expectingthisto still point at the original object. - Defining an object’s method as an arrow function, which prevents it from having its own
this. - Forgetting that
thisdepends on how a function is called, not on where it was defined (except for arrow functions). - Not using
bind(),call(), orapply()when a method truly needs to be passed around separately from its object.
Best Practices for this
- Use regular functions (or class methods) for object methods, since they need their own dynamic
this. - Use arrow functions for callbacks defined inside a method when you want them to keep the surrounding
this. - Use
bind()when a method must be passed as a callback elsewhere and still needs its originalthis. - Never define an object’s own methods as arrow functions.
- When in doubt, log
this(or checkthis === expectedObject) to confirm what it actually refers to.
Quick Summary
- The value of
thisdepends on how a function is called, not where it is written. - In a method call (
obj.method()),thisis the object before the dot. - Extracting a method and calling it separately loses that connection.
call()andapply()invoke a function with a specificthisimmediately;bind()creates a new function permanently bound to it.- Arrow functions do not have their own
this— they inherit it from the surrounding scope. - Inside a class,
thisrefers to the instance a method was called on. - Regular function callbacks (like in
setTimeout) commonly losethis; arrow function callbacks usually do not.
Try It Yourself
Modify the code below. Try removing .bind(counter)
to see this break again, or changing the arrow
function back into a regular function inside
increment().
What this code does:
A practice example that keeps this correct inside a
timer by combining an arrow function with .bind().
class Counter {
constructor() {
this.count = 0;
}
increment() {
console.log("increment() called");
setTimeout(() => {
this.count++;
console.log("Count after delay:", this.count);
}, 30);
}
}
const counter = new Counter();
const incrementFn = counter.increment.bind(counter);
incrementFn();
incrementFn();
console.log("Both increment calls have been scheduled");
class Counterconstructor() { this.count = 0; }increment() {console.log("increment() called")setTimeout(() => { this.count++; console.log("Count after delay:", this.count); }, 30) — arrow inherits this from increment.} }const counter = new Counter()const incrementFn = counter.increment.bind(counter) — even if increment is passed around, this stays bound to counter. The arrow inside then still sees the correct this.incrementFn(); incrementFn();console.log("Both increment calls have been scheduled") — combining bind (for the method itself) and an arrow (for the inner callback) is a robust pattern when methods are used as callbacks.
Tip:
Ask “how is this function being called?” rather than “where was
this function written?” — except for arrow functions, where the
opposite is true: they always use the this from
where they were defined.
Understanding this explains behavior you have
already seen in object methods, classes, and inheritance, and
is essential before moving on to closures and more advanced
function behavior in JavaScript.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.