Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save carefree-ladka/3ac9ff14a17b928a2dd9ae62de4218bf to your computer and use it in GitHub Desktop.

Select an option

Save carefree-ladka/3ac9ff14a17b928a2dd9ae62de4218bf to your computer and use it in GitHub Desktop.
The 'this' Keyword in JavaScript - Complete Guide

The 'this' Keyword in JavaScript - Complete Guide

Table of Contents

  1. Introduction
  2. What is 'this'?
  3. How 'this' is Determined
  4. Usage Contexts
  5. Explicit Binding
  6. Common Pitfalls
  7. Best Practices
  8. Summary

Introduction

The this keyword is one of JavaScript's most powerful yet confusing features. It's a special identifier that's automatically defined in the scope of every function, and its value depends on how the function is called.

What is 'this'?

this is a runtime binding that refers to the context in which a function is executed. Unlike other programming languages where this always refers to the instance of a class, JavaScript's this is dynamic and determined by the call-site (how the function is invoked).

How 'this' is Determined

The value of this follows these rules in order of precedence:

  1. new binding - When used with new, this refers to the newly created object
  2. Explicit binding - When using call(), apply(), or bind()
  3. Implicit binding - When called as a method of an object
  4. Default binding - Falls back to global object (or undefined in strict mode)

Usage Contexts

Global Context

In the global execution context, this refers to the global object.

console.log(this); // Window (in browsers) or global (in Node.js)

function showThis() {
  console.log(this);
}
showThis(); // Window (non-strict) or undefined (strict mode)

Function Context

In regular functions, this depends on how the function is called.

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

regularFunction(); // Window (non-strict) or undefined (strict mode)

'use strict';
function strictFunction() {
  console.log(this);
}
strictFunction(); // undefined

Method Context

When a function is called as a method of an object, this refers to that object.

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

person.greet(); // "Hello, I'm Alice"

const greetFunc = person.greet;
greetFunc(); // "Hello, I'm undefined" - loses context!

Constructor Context

When a function is used as a constructor with new, this refers to the newly created instance.

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.introduce = function() {
    console.log(`I'm ${this.name}, ${this.age} years old`);
  };
}

const alice = new Person('Alice', 30);
alice.introduce(); // "I'm Alice, 30 years old"

Arrow Functions

Arrow functions don't have their own this binding. They inherit this from the enclosing lexical context.

const obj = {
  name: 'Object',
  regularFunc: function() {
    console.log(this.name); // 'Object'
    
    const arrowFunc = () => {
      console.log(this.name); // 'Object' - inherits from regularFunc
    };
    arrowFunc();
  },
  arrowMethod: () => {
    console.log(this.name); // undefined - inherits from global scope
  }
};

obj.regularFunc();
obj.arrowMethod();

Event Handlers

In DOM event handlers, this typically refers to the element that received the event.

const button = document.querySelector('button');

button.addEventListener('click', function() {
  console.log(this); // The button element
  this.classList.add('clicked');
});

// With arrow function
button.addEventListener('click', () => {
  console.log(this); // Window or undefined - not the button!
});

Class Context

In ES6 classes, this refers to the instance of the class.

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
  
  delayedSpeak() {
    setTimeout(() => {
      // Arrow function preserves 'this' from the class method
      console.log(`${this.name} makes a delayed sound`);
    }, 1000);
  }
}

const dog = new Animal('Dog');
dog.speak(); // "Dog makes a sound"
dog.delayedSpeak(); // "Dog makes a delayed sound" (after 1s)

Explicit Binding

JavaScript provides methods to explicitly set the value of this.

call()

Invokes a function with a specified this value and arguments provided individually.

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

const person = { name: 'Bob' };
greet.call(person, 'Hello', '!'); // "Hello, Bob!"

apply()

Similar to call(), but arguments are provided as an array.

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

const person = { name: 'Charlie' };
greet.apply(person, ['Hi', '...']); // "Hi, Charlie..."

bind()

Creates a new function with this permanently bound to the specified value.

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

const greetFunc = person.greet;
greetFunc(); // "Hello, I'm undefined"

const boundGreet = person.greet.bind(person);
boundGreet(); // "Hello, I'm Diana"

Common Pitfalls

Losing Context in Callbacks

const counter = {
  count: 0,
  increment: function() {
    this.count++;
  }
};

// Problem
setTimeout(counter.increment, 1000); // 'this' is lost!

// Solutions
setTimeout(() => counter.increment(), 1000); // Arrow function
setTimeout(counter.increment.bind(counter), 1000); // bind()

Methods as Callbacks

class DataFetcher {
  constructor() {
    this.data = [];
  }
  
  fetch() {
    // Problem
    apiCall(this.processData); // 'this' will be wrong
    
    // Solution 1: Arrow function
    apiCall((result) => this.processData(result));
    
    // Solution 2: Bind in constructor
    this.processData = this.processData.bind(this);
    apiCall(this.processData);
  }
  
  processData(result) {
    this.data.push(result);
  }
}

Nested Functions

const obj = {
  name: 'Object',
  method: function() {
    console.log(this.name); // 'Object'
    
    function nested() {
      console.log(this.name); // undefined - different context!
    }
    nested();
    
    // Solution: Use arrow function
    const nestedArrow = () => {
      console.log(this.name); // 'Object'
    };
    nestedArrow();
  }
};

Best Practices

  1. Use arrow functions for callbacks when you want to preserve the outer this context
  2. Bind methods in constructors if they'll be passed as callbacks
  3. Use strict mode to catch accidental global this usage
  4. Avoid using this in arrow functions as methods since they don't have their own this
  5. Be consistent in your codebase - choose a pattern and stick with it
  6. Use class fields with arrow functions for auto-binding in React and similar frameworks
class Component {
  // Auto-bound method using class field
  handleClick = () => {
    console.log(this); // Always refers to the component instance
  }
  
  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

Summary

The this keyword in JavaScript is context-dependent and determined by how a function is called, not where it's defined. Understanding the binding rules (new, explicit, implicit, default) and the special behavior of arrow functions is crucial for mastering JavaScript. When in doubt, use arrow functions for callbacks or explicitly bind methods to ensure this refers to what you expect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment