# How the `this` Keyword Works in JavaScript: Regular Functions, Arrow Functions, and Methods

> Understand JavaScript this binding in regular functions, arrow functions, and methods. Learn how invocation context dictates the value of this, prioritizing binding rules.

- Repository: [Leonardo Maldonado/33-js-concepts](https://github.com/leonardomso/33-js-concepts)
- Tags: deep-dive
- Published: 2026-03-03

---

**In JavaScript, the value of `this` is determined by how a function is invoked rather than where it is defined, following five binding rules that prioritize `new` binding, explicit binding, implicit binding, and default binding, while arrow functions capture `this` lexically from their surrounding scope.**

Understanding how the `this` keyword works in JavaScript is essential for mastering object-oriented patterns and avoiding common context-related bugs. The `leonardomso/33-js-concepts` repository provides an exhaustive test suite in [`tests/object-oriented/this-call-apply-bind/this-call-apply-bind.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/this-call-apply-bind/this-call-apply-bind.test.js) that demonstrates these behaviors through executable specifications. This guide explains the five binding rules that govern `this` across different invocation contexts based on the actual source code implementation.

## The Five Binding Rules for `this`

JavaScript determines the value of `this` through five distinct binding rules, each triggered by a specific invocation pattern. The test suite in [`this-call-apply-bind.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/this-call-apply-bind.test.js) validates these rules with test cases such as "should have different `this` values depending on how function is called" and "Rule 5: Arrow Functions (Lexical this)".

### Default Binding in Standalone Functions

When a regular function is invoked without any context—simply as a standalone function—**default binding** applies. In strict mode, `this` is set to `undefined`; in non-strict mode, it defaults to the global object.

```javascript
function showThis() { 
  return this; 
}

// In strict mode
expect(showThis()).toBeUndefined();

```

### Implicit Binding in Method Calls

When a function is called as a property of an object using dot notation (`obj.method()`), **implicit binding** sets `this` to the object that appears immediately before the dot. This rule applies to regular functions assigned as object properties.

```javascript
const obj = { 
  name: 'Object',
  showThis: function() { return this; }
};

expect(obj.showThis()).toBe(obj);

```

### Explicit Binding with `call`, `apply`, and `bind`

JavaScript provides three methods for manually specifying the `this` context: **explicit binding**. The `call()` method invokes the function immediately with a given `this` value and arguments passed individually, while `apply()` accepts arguments as an array. The `bind()` method returns a new function with `this` permanently fixed, ignoring subsequent attempts to rebind.

```javascript
function greet() { 
  return `Hello, ${this.name}!`; 
}

const alice = { name: 'Alice' };
expect(greet.call(alice)).toBe('Hello, Alice!');

function introduce(greeting, punctuation) {
  return `${greeting}, I'm ${this.name}${punctuation}`;
}

const bob = { name: 'Bob' };
expect(introduce.apply(bob, ['Hi', '!'])).toBe('Hi, I\'m Bob!');

function getName() { 
  return this.name; 
}

const boundGetName = getName.bind(alice);
expect(boundGetName()).toBe('Alice');
expect(boundGetName.call({ name: 'Bob' })).toBe('Alice'); // call ignored

```

### `new` Binding in Constructors

When a function is invoked with the `new` keyword, JavaScript creates a fresh object and binds `this` to that new instance. This **new binding** takes precedence over all other binding types and allows constructors to initialize instance properties.

```javascript
class Rectangle {
  constructor(w, h) { 
    this.width = w; 
    this.height = h; 
  }
  getArea() { 
    return this.width * this.height; 
  }
}

const rect = new Rectangle(10, 5);
expect(rect.getArea()).toBe(50);

```

### Lexical Binding in Arrow Functions

**Arrow functions** do not have their own `this` binding. Instead, they capture the `this` value from their enclosing lexical scope at definition time. This makes arrow functions immune to rebinding via `call`, `apply`, or `bind`, and ideal for preserving context in callbacks.

```javascript
const obj = {
  name: 'Object',
  method() {
    const arrow = () => this.name;
    return arrow();
  }
};

expect(obj.method()).toBe('Object');

```

Arrow functions ignore explicit binding attempts:

```javascript
const obj = {
  name: 'Original',
  getArrow() { 
    return () => this.name; 
  }
};

const arrow = obj.getArrow();
expect(arrow.call({ name: 'Other' })).toBe('Original');

```

## Binding Priority and Rule Precedence

The five binding rules follow a strict hierarchy of precedence: **new binding > explicit binding > implicit binding > default binding**. Arrow functions operate outside this hierarchy entirely, as they close over the lexical `this` from their creation context rather than participating in runtime binding determination.

## How `this` Works in Different Function Types

### Regular Functions and Dynamic Context

Regular functions utilize **dynamic binding**, meaning the value of `this` depends entirely on the invocation pattern at runtime. The same function can reference different objects as `this` when called as a method versus a standalone function, as demonstrated in the test "should have different `this` values depending on how function is called".

### Arrow Functions and Lexical Scope

Arrow functions inherit `this` from their enclosing scope, making them predictably bound to their creation context. This behavior is particularly valuable for callback functions where preserving the outer method's context is necessary.

```javascript
class Counter {
  constructor() { 
    this.count = 0; 
  }
  
  incrementWithArrow() {
    [1, 2, 3].forEach(() => { 
      this.count++; 
    });
  }
}

const c = new Counter();
c.incrementWithArrow();
expect(c.count).toBe(3);

```

## Source Code Reference

The implementation details and test cases for these binding rules are located in [`tests/object-oriented/this-call-apply-bind/this-call-apply-bind.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/this-call-apply-bind/this-call-apply-bind.test.js) within the `leonardomso/33-js-concepts` repository. This file contains exhaustive unit tests covering edge cases for all five binding rules, arrow function behavior, and interaction between different invocation patterns. The [`index.js`](https://github.com/leonardomso/33-js-concepts/blob/main/index.js) entry point exposes these modules as part of the package build, ensuring the examples are validated against the library's public API.

## Summary

- **Invocation determines binding**: The value of `this` depends on how a function is called, not where it is defined.
- **Five binding rules govern `this`**: Default, implicit, explicit, `new`, and lexical binding each apply to specific invocation patterns.
- **Arrow functions capture lexical `this`**: They inherit `this` from their surrounding scope and ignore attempts to rebind with `call`, `apply`, or `bind`.
- **Binding precedence matters**: `new` binding overrides explicit binding, which overrides implicit binding, which overrides default binding.
- **Test suite validation**: The `33-js-concepts` repository provides executable proof of these behaviors in [`this-call-apply-bind.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/this-call-apply-bind.test.js).

## Frequently Asked Questions

### What is the difference between `this` in arrow functions and regular functions?

Regular functions use dynamic binding where `this` is determined by how the function is invoked at runtime, while arrow functions use lexical binding where `this` is captured from the surrounding scope at definition time. Arrow functions cannot be rebound using `call`, `apply`, or `bind`, making them ideal for callbacks where the outer context must be preserved.

### How does the `new` keyword affect `this` binding?

When a function is invoked with `new`, JavaScript creates a new empty object, sets `this` to reference that object within the constructor, and returns the object implicitly unless the constructor returns a different object explicitly. This **new binding** has the highest priority and overrides both explicit and implicit binding rules.

### Can you change `this` inside an arrow function using `call` or `bind`?

No, arrow functions do not have their own `this` binding and therefore ignore attempts to set `this` via `call`, `apply`, or `bind`. According to the test cases in [`this-call-apply-bind.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/this-call-apply-bind.test.js), calling an arrow function with `call({ name: 'Other' })` will still return the lexical `this` value captured at creation time, not the object passed to `call`.

### Why is `this` undefined in my JavaScript function?

If `this` is `undefined` in a standalone function call, your code is likely running in **strict mode** where default binding sets `this` to `undefined` rather than the global object. This is the expected behavior for default binding in modern JavaScript and helps prevent accidental global variable pollution.