# The `this` Keyword — Part 1: Binding Rules

## 1. The Core Idea

`this` is not fixed by where a function is *defined* — it is determined by **how the function is called**. The same function can have a different `this` on every call. There are four binding rules, checked in priority order.

## 2. Rule 1 — Default Binding (plain function call)

```js
function whoAmI() {
  console.log(this);
}
whoAmI();
// non-strict mode: globalThis (window / global)
// strict mode ("use strict"): undefined
```

Calling a function with no receiver (`fn()`, not `obj.fn()`) triggers default binding. **Always assume strict mode** in modern code (ES modules are strict by default) — `this` will be `undefined`, not the global object.

## 3. Rule 2 — Implicit Binding (method call)

```js
const user = {
  name: "Ana",
  greet() {
    console.log(`Hi, I'm ${this.name}`);
  },
};
user.greet(); // "Hi, I'm Ana" — this === user
```

Whatever object is **immediately to the left of the dot** at the call site becomes `this`. This is purely about the **call site**, not where the function was defined.

```js
const greetFn = user.greet;
greetFn(); // "Hi, I'm undefined" — this === undefined (strict) — lost its receiver!
```

This is the single most common `this` bug: extracting a method as a standalone reference loses implicit binding.

## 4. Rule 3 — Explicit Binding (`call`, `apply`, `bind`)

```js
function greet() {
  console.log(`Hi, I'm ${this.name}`);
}
const person = { name: "Leo" };

greet.call(person);           // "Hi, I'm Leo" — invoke now, this = person
greet.apply(person);          // same as call, but arguments passed as an array
const bound = greet.bind(person);
bound();                       // "Hi, I'm Leo" — this permanently fixed to person
```

| Method | Invokes immediately? | Arguments | `this` |
|---|---|---|---|
| `.call(thisArg, a, b, ...)` | ✅ yes | comma-separated | set to `thisArg` |
| `.apply(thisArg, [a, b])` | ✅ yes | array | set to `thisArg` |
| `.bind(thisArg, a, b, ...)` | ❌ no — returns a new function | pre-filled, then more allowed at call time | permanently set to `thisArg` |

`bind` creates a new function whose `this` **cannot be overridden later**, even by implicit binding.

## 5. Rule 4 — `new` Binding (constructor call)

```js
function User(name) {
  this.name = name;
}
const u = new User("Mia");
console.log(u.name); // "Mia"
```

`new` creates a brand-new empty object, sets `this` to that object inside the function, and (if the function doesn't explicitly return an object) returns it automatically.

## 6. Priority Order

When multiple rules could apply, this order wins:

```
new binding  >  explicit binding (call/apply/bind)  >  implicit binding (obj.method())  >  default binding (plain call)
```

![Priority order: new binding beats explicit binding beats implicit binding beats default binding](diagrams/01-binding-priority.svg)

A function created with `bind()` **cannot** have its `this` changed by `new` in most engines' practical usage patterns for regular functions — but `bind`-produced functions can still be used as constructors in edge cases; this is rarely relied upon in real code.

## 7. Under the Hood: `this` as a Hidden Call Argument

`this` isn't resolved by searching a scope chain the way a normal variable is — it's set up fresh on every single function **invocation**, as part of building that call's execution context. Mechanically, engines pass the receiver as an extra, hidden argument alongside the explicit ones — the calling convention itself carries `this`, not the variable-lookup machinery from the Scope topic.

```
A call like  user.greet()   compiles to roughly:
  CallProperty  greet_function, receiver=user, args=[]
                                  ^^^^^^^^^^^^
                          `this` travels as part of the CALL instruction itself,
                          not as a value looked up inside greet()'s body
```

This is *why* the four binding rules are checked at the **call site**, not inside the function: the bytecode for calling a function differs depending on whether it's a bare call (`fn()`), a property call (`obj.fn()`), or a `new` call — each emits a different instruction that supplies (or omits) the receiver differently, before `greet`'s body ever starts running.

### Derived class constructors: `this` literally does not exist yet

The spec formalizes this with an internal `[[ThisBindingStatus]]` slot on every execution context:

| Function kind | `[[ThisBindingStatus]]` | Meaning |
|---|---|---|
| Regular function / base class constructor | `initialized` | `this` is usable immediately |
| Arrow function | `lexical` | no own `this` at all — inherited from the enclosing context (full detail in Part 2) |
| Derived class constructor (`extends`) | `uninitialized` | `this` doesn't exist until `super()` runs |

```js
class Dog extends Animal {
  constructor(name) {
    console.log(this); // ReferenceError: must call super constructor before accessing 'this'
    super(name);          // THIS call is what actually creates `this` and flips the status to initialized
  }
}
```

This is a genuinely different runtime state from a plain TDZ (Variables and Hoisting topics) — `this` in a derived constructor isn't just "uninitialized," it's a slot that the `super()` call itself populates, by receiving the object constructed further up the inheritance chain.

Continue to `js_this_keyword_theory_2.md` for arrow functions and practical pitfalls.
