# The Magic of this, call(), apply(), and bind() in JavaScript

If you ask any JavaScript developer what the most confusing part of the language is, there is a very high chance they will say the `this` keyword.

It behaves differently depending on where you use it, how you call it, and whether or not you are in strict mode. But once you understand the simple mental model behind it, the mystery disappears. And once you master the tools to manipulate it—`call()`, `apply()`, and `bind()`—you unlock a whole new level of flexibility in your code.

Here is your straightforward, jargon-free guide to understanding context in JavaScript.

* * *

### What does `this` actually mean?

Instead of diving into deep compiler internals and execution contexts, let’s use a simple golden rule:

`this` **simply refers to "who is calling the function right now."**

Imagine a function is a phone call. If you pick up the phone and ask, "Who is this?", the answer depends entirely on who dialed your number.

In JavaScript, `this` is a dynamic reference to the object that invoked (called) the function.

#### 1\. `this` Inside Objects

When a function is stored inside an object, it is called a **method**. When you call that method, `this` points directly to the object that holds it.

```javascript
const user = {
  name: "Alice",
  age: 25,
  introduce: function() {
    // "this" refers to the 'user' object because 'user' called the function
    console.log(`Hi, I am ${this.name} and I am ${this.age} years old.`);
  }
};

user.introduce(); 
// Output: Hi, I am Alice and I am 25 years old.
```

*Who is calling the function?* The `user` object. Therefore, `this.name` translates to `user.name`.

#### 2\. `this` Inside Normal (Standalone) Functions

When you call a function that is not a property of an object—just a plain function call—`this` depends on strict mode:

*   In non-strict mode, `this` falls back to the global object (in browsers, `window` or `globalThis`).
    
*   In strict mode, `this` becomes `undefined`.
    

Examples:

```js
function show() {
  console.log(this);
}

show(); // non-strict: window (in browser)
        // strict: undefined (if 'use strict' at top)
```

Why that matters: if you expect `this` to refer to some object but you call the function without that object as the caller, you get the global object or `undefined` instead—leading to bugs (e.g., accidentally creating globals, or TypeErrors when accessing properties on `undefined`).

Common gotcha: extracting a method out of its object:

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

const greet = user.greet;
greet(); // undefined (strict) or '' (global.name) — you lost the user binding
```

To preserve the intended `this`, use explicit binding (next section), call it as a method (`user.greet()`), or use an arrow function bound in the right scope.

#### 3\. `this` with Constructors and `new`

When you call a function with `new`, JavaScript:

1.  Creates a fresh object,
    
2.  Sets `this` inside the function to that new object,
    
3.  Returns the object (unless the function explicitly returns another object).
    

Example:

```js
function Person(name) {
  this.name = name;
}
const p = new Person('Lin');
console.log(p.name); // "Lin"
```

Important precedence rule (short): "new" binding takes priority over implicit/explicit binding. If you try to force a `this` with `call`/`apply` on a constructor, `new` will still create the instance and use it as `this` instead.

#### 4\. `this` in Arrow Functions (lexical `this`)

Arrow functions do not have their own `this`. Instead they inherit `this` from the surrounding (lexical) scope — like closures for `this`.

```js
const obj = {
  value: 42,
  regular() { console.log(this.value); },      // 42
  arrow: () => console.log(this.value)         // likely undefined or outer scope value
};

obj.regular(); // 42
obj.arrow();   // not 42 — arrow's `this` is set where the function was defined
```

Use arrow functions when you want to preserve the outer `this` (commonly used in callbacks), but do NOT use them as object methods or constructors.

#### 5\. Explicit Binding: call(), apply(), bind()

These are the explicit tools to set `this` for a function.

*   `fn.call(thisArg, ...args)` — call `fn` immediately with `thisArg` and a list of args.
    
*   `fn.apply(thisArg, argsArray)` — call `fn` immediately with `thisArg` and arguments as an array.
    
*   `fn.bind(thisArg, ...boundArgs)` — return a NEW function permanently bound to `thisArg` (and optionally pre-filled args).
    

Examples and typical use cases:

Borrowing a method:

```js
const obj = { name: 'Ada' };
function say(greeting) {
  console.log(greeting + ', ' + this.name);
}

say.call(obj, 'Hello');           // "Hello, Ada"
say.apply(obj, ['Hi']);           // "Hi, Ada"
```

Using `apply` with arrays (common pre-spread era):

```js
const nums = [3, 1, 4];
Math.max.apply(null, nums);       // 4
// Modern alternative: Math.max(...nums)
```

Partial application with `bind`:

```js
function multiply(a, b) { return a * b; }
const double = multiply.bind(null, 2);
console.log(double(5)); // 10
```

Keeping method `this` when passing as callback:

```js
class Button {
  constructor() {
    this.count = 0;
    this.onClick = this.onClick.bind(this); // ensure `this` inside onClick is the instance
  }
  onClick() {
    this.count++;
    console.log(this.count);
  }
}
const btn = new Button();
setTimeout(btn.onClick, 100); // Works because bound
```

Notes:

*   `call` vs `apply`: same effect; `apply` accepts arguments as an array.
    
*   `bind` returns a new function; use it when you need a stable `this` for future calls (event handlers, callbacks).
    
*   Historically, `null`/`undefined` passed to `call`/`apply` default to global object in non-strict mode; in strict mode they remain `null`/`undefined`.
    

Precedence summary (most to least):

1.  `new`
    
2.  `call`/`apply`/`bind` (explicit)
    
3.  method-call (implicit)
    
4.  global/default
    

Edge detail: using `bind` and then `new` — the bound `thisArg` is ignored when constructing; the new instance becomes `this`, but bound arguments remain prepended.

#### 6\. Practical Patterns & Real-World Examples

*   Method borrowing:
    
    *   Use `Array.prototype.slice.call(arguments)` (older pattern) to convert `arguments` to array. Now prefer `Array.from(arguments)` or rest params.
        
*   Event handlers in classes:
    
    *   Bind handlers in constructor, or use class fields with arrow functions:
        
        ```js
        handleClick = () => { console.log(this); }
        ```
        
*   Partial application:
    
    *   `bind` is a simple built-in way to pre-fill arguments.
        
*   Function utilities:
    
    *   `call/apply` let you reuse generic functions on different objects.
        

#### 7\. Common Pitfalls & How to Avoid Them

*   Losing `this` when passing methods as callbacks — use `bind`, arrow functions, or wrap in another function.
    
*   Incorrect assumptions about `this` inside arrow functions — remember arrow functions get `this` lexically.
    
*   Accidentally relying on global `this` in non-strict mode — always use strict mode or prefer explicit bindings.
    
*   Overusing `bind` for performance-critical hot paths — binds create new functions each time; cache the bound function if reused.
    

#### 8\. Quick Cheat Sheet

*   Method call: `obj.fn()` → `this` is `obj`.
    
*   Function call: `fn()` → non-strict: global; strict: `undefined`.
    
*   Constructor: `new Fn()` → `this` is the new instance.
    
*   Arrow function: `() => {}` → `this` is inherited from outer scope.
    
*   Explicit: `fn.call(obj, ...)`, `fn.apply(obj, [...])`, `fn.bind(obj)` → force `this`.
    

Conclusion

Understanding `this` becomes simple if you apply the golden rule: think about "who is calling the function right now" and remember the four primary binding ways (default, implicit, explicit, and `new`), plus the special lexical behavior of arrow functions. Once you can identify which binding is in effect, `call`, `apply`, and `bind` give you the tools to control or preserve that binding when you need to.
