JavaScript `this` Binding

The value of `this` is determined mainly by how a function is invoked, not where the function is defined. Arrow functions are the key exception: they capture `this` lexically from their surrounding scope.

In a regular function, `this` is determined at call time. A function called as `obj.method()` receives `obj` as `this`, while a standalone call does not receive an object as its receiver. In strict mode, a standalone regular function call has `this` set to `undefined`; non-strict behavior can differ because of legacy coercion rules.

When a function is called with `new`, JavaScript creates a new object and binds that object as `this` while the constructor function runs. Explicit binding with `call`, `apply`, or `bind` lets code choose the `this` value for a regular function invocation.

Arrow functions do not create their own `this`. They use the `this` from the surrounding lexical scope, so `call`, `apply`, and `bind` cannot replace an arrow function's captured `this`.

A common frontend bug occurs when a method is passed as a callback. Extracting `const fn = obj.method` and then calling `fn()` removes the original receiver, so the method no longer gets `obj` as `this`. Binding the method, wrapping it in an arrow function, or otherwise preserving the receiver can solve the problem.

Key points

Interview questions

Why do arrow functions behave differently with `this`?

Arrow functions do not define their own `this` binding. Instead, they resolve `this` from the surrounding lexical environment. This makes arrows useful for callbacks that need to retain the surrounding method's `this`. It also means `call`, `apply`, and `bind` cannot replace an arrow function's captured `this`.

What determines the value of `this` in a JavaScript function?

For a regular function, `this` is primarily determined by how the function is called. A method call such as `obj.fn()` supplies `obj` as the receiver. A standalone call follows the strict-mode or non-strict-mode rules for `this`. Arrow functions are different because they inherit `this` from their surrounding lexical scope.

Why can `const fn = obj.method; fn()` produce a different result from `obj.method()`?

In `obj.method()`, the expression before the dot supplies the receiver, so `this` is `obj`. When the method is assigned to `fn`, the function is detached from that receiver. Calling `fn()` is therefore a standalone invocation and does not automatically restore `obj` as `this`. A common fix is `obj.method.bind(obj)` or an arrow wrapper such as `() => obj.method()`.

What is the difference between `call`, `apply`, and `bind`?

`call` invokes a regular function immediately with a specified `this` and arguments passed individually. `apply` also invokes it immediately, but receives the arguments through an array-like value. `bind` does not invoke the function immediately; it returns a new function with a predetermined `this` and optionally some preset arguments. These mechanisms are useful when a function needs an explicit receiver.

How does `new` affect `this`?

A constructor call with `new` creates a new object and invokes the constructor with that object as `this`. The constructor can therefore initialize instance properties through `this`. If the constructor explicitly returns an object, that returned object can become the result of the `new` expression; otherwise the newly created instance is returned. Arrow functions cannot be used as constructors because they do not provide the constructor behavior required by `new`.