Skip to main content

Command Palette

Search for a command to run...

Understanding the this Keyword in JavaScript

Updated
13 min read
Understanding the this Keyword in JavaScript
H
CS undergrad | Tech enthusiast | Focusing on Web Dev • DSA • ML | Building skills for real-world impact

this = most confusing JavaScript concept. What this refer to? Depend on who call function. Caller = this. Understand caller, understand this.

This about this keyword and how it change.


What this Represents

this = reference to caller. Who call function? That who this is.

Simple Definition

this = the object that calling the function. Not the function itself. The thing that call it.

Real Analogy

Waiter in restaurant:

Customer say "I want coffee"
Waiter say "Who want coffee?"
Customer say "Me, I want coffee"

So "I" = the customer (the caller)

In JavaScript:

user.greet()  // user calling greet()
              // So "this" inside greet = user

this = who call the method.

Why this Matter

this let function know who calling it. Function can behave different for different callers.

const user1 = {
  name: 'Alice',
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

const user2 = {
  name: 'Bob',
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

user1.greet();  // "Hello, I'm Alice"
                // this = user1

user2.greet();  // "Hello, I'm Bob"
                // this = user2

Same function logic. Different this. Different output.


this in Global Context

In global scope, this = global object.

Browser Global

In browser, global object = window.

console.log(this);  // window (in browser)
console.log(this === window);  // true

console.log(this.innerHeight);  // window.innerHeight

Top level code run in window context. So this = window.

Node.js Global

In Node.js, global object = global (or module context).

console.log(this);  // {} (module context in Node.js)
                    // NOT global in strict module

console.log(global);  // global object

Node.js = different. But usually don't use this in global scope.

Function in Global Scope

function greet() {
  console.log(this);
}

greet();  // Browser: window
          // Node.js: global (in strict mode: undefined)

No caller = default caller = global object.

Problem: Losing this

const user = {
  name: 'Alice',
  greet() {
    console.log(this.name);
  }
};

const func = user.greet;  // Extract method
func();  // undefined (or error)

// Why?
// func() have no caller object
// So this = global or undefined
// global.name = undefined

When extract method = lose context = lose this.


this Inside Objects

this = the object calling method.

Method Call

Object call its own method. this = object.

const user = {
  name: 'Alice',
  age: 25,
  
  greet() {
    console.log(`I'm ${this.name}`);
  },
  
  haveBirthday() {
    this.age++;
    console.log(`Now ${this.age}`);
  }
};

user.greet();         // this = user
                      // "I'm Alice"

user.haveBirthday();  // this = user
                      // Now 26

user.greet() = user calling greet = this = user.

Accessing Object Properties

Method use this to access own properties.

const car = {
  brand: 'Toyota',
  speed: 0,
  
  accelerate() {
    this.speed += 10;
    console.log(`\({this.brand} speed: \){this.speed}`);
  }
};

car.accelerate();  // "Toyota speed: 10"
car.accelerate();  // "Toyota speed: 20"

console.log(car.speed);  // 20

this.speed refer to car's speed property. this.brand refer to car's brand property.

Multiple Objects, Same Method

const person1 = {
  name: 'Alice',
  introduce() {
    console.log(`I'm ${this.name}`);
  }
};

const person2 = {
  name: 'Bob',
  introduce() {
    console.log(`I'm ${this.name}`);
  }
};

person1.introduce();  // "I'm Alice" (this = person1)
person2.introduce();  // "I'm Bob"   (this = person2)

Different objects, same method logic. this different.

Nested Objects

const company = {
  name: 'TechCorp',
  owner: {
    name: 'Alice',
    greet() {
      console.log(`I'm ${this.name}`);  // this = owner (not company)
    }
  }
};

company.owner.greet();  // "I'm Alice"
                        // this = owner object (immediate caller)

this = immediate caller. Not parent object.


this Inside Functions

Function context different from object context.

Regular Function Call

function greet() {
  console.log(this);
}

greet();  // Browser: window
          // Node.js: undefined (strict mode) or global

// No object call it = default context

Function standalone = no object caller = this = global or undefined.

Function with Caller

const user = { name: 'Alice' };

function greet() {
  console.log(`Hi, I'm ${this.name}`);
}

// Call function with user context
greet.call(user);  // "Hi, I'm Alice"
                   // Force this = user

.call() = force who caller is.

Method vs Function

const user = {
  name: 'Alice',
  
  method() {
    console.log(this.name);  // this = user (method call)
  },
  
  getFunction() {
    function inner() {
      console.log(this.name);  // this = global/undefined (function call)
    }
    return inner;
  }
};

user.method();        // "Alice" (this = user)

const func = user.getFunction();
func();               // undefined (this = global)

Method = this = caller. Nested function = this = global (lose context).


How Calling Context Change this

this depend on how function called. Three way to call function.

1. Method Call: obj.func()

Object call function. this = object.

const user = {
  name: 'Alice',
  greet() {
    console.log(`Hi, ${this.name}`);
  }
};

user.greet();  // this = user

2. Function Call: func()

Direct call. this = global (or undefined in strict).

function greet() {
  console.log(this);
}

greet();  // this = window (browser) or undefined (strict mode)

3. Using .call() or .apply()

Explicitly set this.

const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };

function greet() {
  console.log(`Hi, ${this.name}`);
}

greet.call(user1);   // this = user1 → "Hi, Alice"
greet.call(user2);   // this = user2 → "Hi, Bob"

.call(obj) = call function with this = obj.

Difference Visualized

user.greet()
  │
  └─ user calling greet
     └─ this = user

greet()
  │
  └─ no one calling (function call)
     └─ this = global

greet.call(user)
  │
  └─ explicitly set caller
     └─ this = user

Same function. Different caller. Different this.


Arrow Functions and this

Arrow function different from regular function. Arrow function = inherit this from outer scope.

Regular Function

const user = {
  name: 'Alice',
  
  greet() {
    function inner() {
      console.log(this);  // this = global (function call)
    }
    inner();
  }
};

user.greet();  // this inside inner = global (lose context)

Nested function = lose this.

Arrow Function

const user = {
  name: 'Alice',
  
  greet() {
    const inner = () => {
      console.log(this);  // this = user (inherit from greet)
    };
    inner();
  }
};

user.greet();  // this inside inner = user (keep context)

Arrow function = inherit this from parent scope.

When Use Arrow vs Regular

const user = {
  name: 'Alice',
  
  // Regular function = this = user
  greet() {
    console.log(this.name);  // "Alice"
  },
  
  // Arrow function = this inherit from outer
  introduce: () => {
    console.log(this.name);  // undefined (this = window/global)
  }
};

user.greet();      // "Alice" (use regular function in object)
user.introduce();  // undefined (arrow lose this context)

Rule:

  • Object method = use regular function (get this = object)
  • Nested callback = use arrow function (inherit this)

Practical Example

const button = {
  label: 'Click me',
  
  // ✗ Wrong - regular function lose this
  setupWrong() {
    document.getElementById('btn').addEventListener('click', function() {
      console.log(this.label);  // this = button element (wrong)
                                // not this button object
    });
  },
  
  // ✓ Right - arrow function keep this
  setupRight() {
    document.getElementById('btn').addEventListener('click', () => {
      console.log(this.label);  // this = button object (right)
                                // inherited from setupRight
    });
  }
};

Event listener = arrow function keep this.


Common this Mistakes

Mistake 1: Losing this in Callback

const user = {
  name: 'Alice',
  
  loadData(callback) {
    setTimeout(callback, 1000);
  },
  
  fetch() {
    this.loadData(function() {
      console.log(this.name);  // this = undefined (function call)
    });
  }
};

user.fetch();  // undefined (wrong)

Fix with arrow function:

user.fetch() {
  this.loadData(() => {
    console.log(this.name);  // this = user (right)
  });
}

Mistake 2: Extracting Method Without Context

const user = {
  name: 'Alice',
  greet() {
    console.log(this.name);
  }
};

const greet = user.greet;  // Extract method
greet();  // undefined (lose this)

Fix with .bind():

const greet = user.greet.bind(user);  // Keep this = user
greet();  // "Alice" (right)

.bind() = permanently set this.

Mistake 3: Confusing this in Constructor

function User(name) {
  this.name = name;
  
  this.greet = function() {
    console.log(this.name);  // this = user instance
  };
}

const user = new User('Alice');
user.greet();  // "Alice" (this = user)

new = call function as constructor = this = new instance.

Mistake 4: this in Arrow Function at Object Level

// ✗ Wrong - arrow function at object level
const user = {
  name: 'Alice',
  
  greet: () => {
    console.log(this.name);  // this = window/global
                             // not user object
  }
};

user.greet();  // undefined (wrong)

// ✓ Right - regular function
const user = {
  name: 'Alice',
  
  greet() {
    console.log(this.name);  // this = user
  }
};

user.greet();  // "Alice" (right)

Object method = use regular function. Not arrow function.

Mistake 5: Assuming this Same in Nested Function

const user = {
  name: 'Alice',
  
  process() {
    console.log(this.name);  // "Alice" (this = user)
    
    function inner() {
      console.log(this.name);  // undefined (this = global)
    }
    
    inner();  // this change!
  }
};

user.process();  // "Alice" then undefined

Nested function = new context = this change.


Methods to Control this

1. Using .call()

Call function with specific this.

function greet(greeting) {
  console.log(`\({greeting}, I'm \){this.name}`);
}

const user = { name: 'Alice' };

greet.call(user, 'Hi');  // "Hi, I'm Alice"
                         // this = user

.call(thisArg, arg1, arg2, ...) = set this and call.

2. Using .apply()

Like .call() but arguments as array.

function greet(greeting, emoji) {
  console.log(`\({greeting}, I'm \){this.name} ${emoji}`);
}

const user = { name: 'Alice' };

greet.apply(user, ['Hi', '👋']);  // "Hi, I'm Alice 👋"
                                   // this = user

.apply(thisArg, [args]) = same as call, but array arguments.

3. Using .bind()

Create new function with locked this.

function greet() {
  console.log(`Hi, ${this.name}`);
}

const user = { name: 'Alice' };

const boundGreet = greet.bind(user);  // Create new function

boundGreet();  // "Hi, Alice"
               // this permanently = user

.bind() = don't call immediately. Return new function.

.call() and .apply() = call immediately. .bind() = return function, call later.

Practical Use: Event Listener

const user = {
  name: 'Alice',
  
  setupButton() {
    const button = document.getElementById('btn');
    
    // ✗ Lose this
    // button.addEventListener('click', this.onClick);
    
    // ✓ Keep this
    button.addEventListener('click', this.onClick.bind(this));
  },
  
  onClick() {
    console.log(`Clicked by ${this.name}`);
  }
};

.bind(this) = keep this in event listener.


this in Different Contexts

Global Scope

console.log(this);  // Browser: window, Node.js: depends on module

Inside Object Method

const obj = {
  method() {
    console.log(this);  // this = obj
  }
};

obj.method();

Inside Regular Function

function func() {
  console.log(this);  // undefined (strict) or global (non-strict)
}

func();

Inside Arrow Function

const arrow = () => {
  console.log(this);  // this = outer scope's this
};

Inside Constructor

function Constructor() {
  console.log(this);  // this = new instance
}

new Constructor();

Inside Class

class MyClass {
  method() {
    console.log(this);  // this = instance
  }
}

const obj = new MyClass();
obj.method();

Inside Class Arrow Method

class MyClass {
  method = () => {
    console.log(this);  // this = instance (arrow method)
  }
}

Practical Examples

Example 1: User Object

const user = {
  name: 'Alice',
  age: 25,
  
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  },
  
  birthday() {
    this.age++;
    console.log(`Happy birthday! Now ${this.age}`);
  },
  
  describe() {
    return `\({this.name} is \){this.age} years old`;
  }
};

user.greet();      // "Hello, I'm Alice"
user.birthday();   // "Happy birthday! Now 26"
console.log(user.describe());  // "Alice is 26 years old"

// All use this = user

Example 2: Button Handler

const app = {
  count: 0,
  
  init() {
    document.getElementById('btn').addEventListener(
      'click',
      this.handleClick.bind(this)  // Keep this = app
    );
  },
  
  handleClick() {
    this.count++;
    console.log(`Clicked ${this.count} times`);
  }
};

app.init();
// Click button → "Clicked 1 times"
// Click button → "Clicked 2 times"

Without .bind(this) = this = button element (wrong).

Example 3: Method Borrowing

const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };

function greet() {
  console.log(`Hi, ${this.name}`);
}

greet.call(user1);   // "Hi, Alice"
greet.call(user2);   // "Hi, Bob"

// Same function, different this, different output

Use .call() to borrow function with different context.

Example 4: Constructor Pattern

function User(name, age) {
  this.name = name;
  this.age = age;
  
  this.greet = function() {
    console.log(`I'm ${this.name}`);
  };
}

const user1 = new User('Alice', 25);
const user2 = new User('Bob', 30);

user1.greet();  // "I'm Alice" (this = user1)
user2.greet();  // "I'm Bob"   (this = user2)

new = create instance, set this = instance.

Example 5: React Class Component

class Counter extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
    
    // Bind to keep this = component
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }
  
  render() {
    return <button onClick={this.handleClick}>Count: {this.state.count}</button>;
  }
}

React = need bind or arrow function to keep this.


this Cheat Sheet

WHO CALL IT?           this = WHAT?
═════════════════      ══════════════════════
obj.method()           obj (the caller object)

func()                 undefined (strict) or global (non-strict)

new Constructor()      new instance

func.call(obj)         obj (explicitly set)

func.bind(obj)()       obj (locked in new function)

inside arrow function  outer scope's this (inherit)

inside event           element (default) or bound object
listener

inside setTimeout()    global (unless arrow)

Practice Assignment

1. Object methods:

// Create user object with:
// - name, email properties
// - greet() method (use this.name)
// - getEmail() method (use this.email)
// Test each method

2. Losing and finding this:

// Create object with method
// Extract method, call it (lose this)
// Fix using .bind()
// Verify this = object again

3. Arrow vs regular:

// Create object method that:
// - Use regular function for this = object
// - Use arrow function inside (inherit this)
// Test both work correctly

4. Event listener:

// Create button handler object
// Add click listener
// Use .bind(this) to keep context
// Verify this = object inside handler

5. Constructor pattern:

// Create User constructor function
// Add method using this.name
// Create 2 instances
// Call method on each (different this each time)

Quick Recap

  • this = reference to caller. Who call function? That who this is.

  • Global context = this = window (browser) or global (Node.js).

  • Inside object method = this = object calling method.

  • Inside function (not method) = this = global or undefined (strict).

  • Caller determine this = same function, different caller, different this.

  • Arrow function = inherit this from outer scope (don't create own).

  • Regular function = create own this (depend on caller).

  • .call(obj) = call function with this = obj (immediately).

  • .apply(obj, [args]) = like call but arguments as array (immediately).

  • .bind(obj) = create new function with this = obj (not immediate).

  • Nested function = lose this. Use arrow function or .bind().

  • Object method = use regular function (get this = object).

  • Event listener = use arrow or .bind() to keep this.

  • Constructor = new set this = new instance.

  • Class method = this = instance automatically.

  • Arrow in object = wrong. Lose this context.

  • Remember = this = who calling the function.

Master this = master JavaScript.


If you enjoyed this article, check out my other blogs on this profile. Connect with me: LinkedIn | GitHub | X (Twitter)