Skip to main content

Command Palette

Search for a command to run...

Understanding this, call(), apply(), and bind() in JavaScript

Learn how this works and how call(), apply(), and bind() help control it.

Updated
5 min read
Understanding this, call(), apply(), and bind() in JavaScript

Understanding this, call(), apply(), and bind() in JavaScript

This guide explains how this works in JavaScript and how Function.prototype.call, apply, and bind let you control a function’s calling context. Includes practical examples, common pitfalls, and quick reference.


Quick overview

  • this is determined by how a function is called, not where it's defined (with important exceptions for arrow functions).

  • call(thisArg, ...args) invokes the function with this set to thisArg and arguments passed individually.

  • apply(thisArg, argsArray) invokes the function with this set to thisArg and arguments passed as an array (or array-like).

  • bind(thisArg, ...args) returns a new function permanently bound to thisArg (and optionally pre-filled arguments).


How this is determined (summary)

  1. Global or standalone function call: in non-strict mode this → global object (window in browsers); in strict mode thisundefined.

  2. Method call (obj.fn()): thisobj.

  3. Constructor call (new Fn()): this → newly created instance.

  4. Explicit binding (call/apply/bind): this → provided thisArg (with caveats when using new with a bound function).

  5. Arrow functions: this is lexically inherited from the surrounding (enclosing) scope — cannot be changed by call, apply, or bind.


Examples

Basic this behavior

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

showThis();               // non-strict: global object; strict: undefined

const obj = { x: 10, showThis };
obj.showThis();           // logs obj

new showThis();           // logs the new instance (an object)

call and apply

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Alice' };

greet.call(person, 'Hello', '!');            // Hello, Alice!
greet.apply(person, ['Hi', '...']);          // Hi, Alice...

Use apply when you have an arguments array (or array-like object):

function sum() {
  return Array.prototype.reduce.call(arguments, (a,b) => a + b, 0);
}

sum.apply(null, [1,2,3]); // 6

bind for fixed this and partial application

const module = {
  x: 42,
  getX() { return this.x; }
};

const unboundGetX = module.getX;
console.log(unboundGetX());            // undefined (or global value)

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());              // 42

// Partial application
function multiply(a, b) { return a * b; }
const double = multiply.bind(null, 2);
double(5); // 10

Arrow functions and this

const obj = {
  value: 1,
  arrow: () => { console.log(this); }, // `this` from surrounding scope, not `obj`
  method() {
    const inner = () => console.log(this); // inherits `this` from method
    inner();
  }
};

obj.arrow();   // not obj; likely global or undefined depending on module/strict context
obj.method();  // logs obj

Important details and gotchas

  • Arrow functions: call, apply, and bind cannot change an arrow function’s this. It's lexically fixed to the enclosing scope.

  • bind returns a new function. The bound function can still be used with new. When used with new, the thisArg provided to bind is ignored; the newly created instance becomes this for the original function — but the prototype chain comes from the original function.

  • If thisArg passed to call/apply/bind is null or undefined, non-strict functions will use the global object; in strict mode, this remains null/undefined.

  • apply is useful for spreading arrays into functions before ... spread syntax existed; nowadays you can use fn(...args) instead:

    • fn.apply(obj, args)fn.call(obj, ...args)fn.apply(obj, args) or simply fn.call(obj, ...args).
  • Performance: Historically, call/apply had slight overhead; modern engines optimize well. Prefer readability and semantics over micro-optimizing these calls.


Common patterns

  • Borrowing methods:
const arrLike = { 0: 'a', 1: 'b', length: 2 };
Array.prototype.slice.call(arrLike); // ['a', 'b']
  • Event handlers:
class Button {
  constructor() {
    this.handleClick = this.handleClick.bind(this); // keep `this` when used as callback
  }
  handleClick() {
    console.log(this); // instance
  }
}
  • Partial application:
const log = (level, msg) => console.log(level, msg);
const info = log.bind(null, 'INFO');
info('Server started');

Quick comparison

Method Runs immediately? Arguments
call() Yes Passed individually
apply() Yes Passed as an array (or array-like)
bind() No — returns a new function Returns a new function; optional pre-filled arguments (partial application)

Example summary:

fn.call(obj, a, b)
fn.apply(obj, [a, b])
const newFn = fn.bind(obj)     // newFn doesn't run immediately
newFn(a, b)                    // runs with `this` bound to obj

Notes:

  • bind can be used for partial application: const double = multiply.bind(null, 2).

  • If a bound function is used with new, the this provided to bind is ignored and the newly created instance becomes this.

  • Arrow functions inherit this lexically and cannot have their this changed using call/apply/bind.


Quick reference

  • call(thisArg, arg1, arg2, ...)

  • apply(thisArg, [arg1, arg2, ...])

  • bind(thisArg, arg1, arg2, ...) → returns new function

When to use:

  • Use call/apply when you want to invoke immediately with an explicit this.

  • Use bind when you want a function that will always use a specific this (or pre-fill arguments) later.


Summary

Understanding how this is set in JavaScript is key to writing correct code. Use call and apply to control this for an immediate call, and bind to create a function permanently tied to a specific this. Remember arrow functions inherit this lexically and cannot be re-bound. With these tools you can handle context, borrow methods, and create reliable callbacks and partial functions.

If you want, I can convert this into a shorter cheat-sheet, expand any section with more examples, or produce runnable examples for browser vs Node.js. Which would you prefer?