The this Keyword Interview Questions & Answers

33 questions Updated 2026-06-17

JavaScript `this` interview questions — how this is bound, arrow vs regular functions, call/apply/bind and common context bugs.

Read the in-depth guideThe "this" Keyword in JavaScript — A Complete Guide to Binding

The single most important idea: in a regular function this is decided by how the function is called, not where it's defined. There are four binding rules, in order of precedence:

  1. new bindingnew Fn() -> this is the brand-new instance.
  2. Explicit bindingfn.call(obj), fn.apply(obj), fn.bind(obj) -> this is the object you pass.
  3. Implicit bindingobj.fn() -> this is obj (the thing left of the dot).
  4. Default binding — a plain fn() -> this is undefined in strict mode, or the global object (window/globalThis) in sloppy mode.
function who() { return this }
const obj = { who }
obj.who()            // obj      (implicit)
who.call('hi')       // 'hi'     (explicit)
new who()            // {}       (new instance)
who()                // undefined in strict mode

Arrow functions are the exception — they ignore all four rules and inherit this lexically (next question).

Arrow functions don't have their own this. They capture it lexically — from the enclosing scope at the moment they're defined — and that binding can never be changed (even call/apply/bind can't reassign it). This makes them perfect for callbacks where you want to keep the surrounding object's this.

class Timer {
  seconds = 0
  start() {
    // arrow inherits `start`'s `this` (the Timer instance)
    setInterval(() => this.seconds++, 1000)

    // a regular function here would be called by the timer with
    // `this` === undefined, so `this.seconds` would throw
  }
}

The flip side: because they lack their own this, arrows are a poor choice for object methods that need to reference the object (this would point to the outer scope, not the object) and they cannot be used as constructors.

All three let you explicitly set this; they differ in when they invoke and how they take arguments:

  • fn.call(thisArg, a, b) — invokes immediately, arguments passed individually.
  • fn.apply(thisArg, [a, b]) — invokes immediately, arguments passed as an array.
  • fn.bind(thisArg, a) — does not invoke; returns a new function with this (and any given args) permanently locked in.
function greet(greeting, mark) {
  return `${greeting}, ${this.name}${mark}`
}
const user = { name: 'Ada' }

greet.call(user, 'Hi', '!')      // 'Hi, Ada!'   (args listed)
greet.apply(user, ['Hi', '!'])   // 'Hi, Ada!'   (args in array)
const greetAda = greet.bind(user) // returns a function
greetAda('Hey', '.')             // 'Hey, Ada.'

Memory aid: Apply = Array; bind = "remember for later." bind is the usual fix for passing a method as a callback without losing its receiver.

Because this is set at call time by the call site, and passing a method somewhere else detaches it from its object. setTimeout(obj.fn) only copies the function — the obj. context is lost, so when the timer later calls it as a plain function, this is undefined (strict) / global (sloppy).

const counter = {
  count: 0,
  inc() { this.count++ },
}

setTimeout(counter.inc, 100)        // `this` is not `counter` -> throws/NaN

setTimeout(counter.inc.bind(counter), 100)  // bind the receiver
setTimeout(() => counter.inc(), 100)        // arrow calls it ON counter

This is exactly why React class components had to do this.handleClick = this.handleClick.bind(this) in the constructor — or use class arrow-field methods, which sidestep the problem.

When you call a function with new, JavaScript does four things: (1) creates a fresh empty object, (2) sets that object's prototype to the constructor's .prototype, (3) binds this to the new object inside the call, and (4) returns that object automatically — unless the constructor explicitly returns its own (non-primitive) object.

function User(name) {
  this.name = name      // `this` is the new instance
  // no return needed — the instance is returned for you
}
const u = new User('Ada')
u.name // 'Ada'

function Weird() {
  this.a = 1
  return { b: 2 } // returning an object overrides the default `this`
}
new Weird() // { b: 2 }  (the `this.a = 1` is discarded)

Forgetting new is a classic bug: calling User('Ada') plainly runs default binding, so this is the global object and you accidentally create a global name.

In sloppy (non-strict) mode, a plain function call defaults this to the global object (window / globalThis). In strict mode — and inside ES modules and class bodies, which are always strict — that same call leaves this as undefined.

function show() { return this }

// sloppy mode
show() // the global object

// strict mode
'use strict'
show() // undefined

This is a feature, not a nuisance: with undefined you get a loud TypeError the moment you accidentally rely on default binding (Cannot read properties of undefined), instead of silently leaking properties onto the global object. It surfaces "I forgot to bind this" bugs immediately.

When multiple rules could apply, they resolve in this order (highest first):

  1. new bindingnew Fn() wins over everything.
  2. Explicit bindingcall/apply/bind.
  3. Implicit bindingobj.method().
  4. Default binding — plain fn() -> undefined/global.
function f() { return this.id }
const obj = { id: 1, f }
const bound = f.bind({ id: 2 })
obj.f()            // 1 (implicit)
bound()            // 2 (explicit beats implicit)
new bound().id     // undefined-> new beats bind (this is the new instance)

Arrow functions sit outside this hierarchy entirely — they ignore all four rules and inherit this lexically.

In a regular function listener, this is the element the handler is attached to (the currentTarget). In an arrow function, this is inherited from the surrounding scope instead.

button.addEventListener('click', function () {
  this // the button element
})
button.addEventListener('click', () => {
  this // whatever `this` was outside (NOT the button)
})

So use a regular function when you want this to be the element; use an arrow (and e.currentTarget) when you need the outer this. This trips people up when converting handlers to arrows.

forEach, map, filter, etc. accept an optional thisArg second argument that sets this for the callback. Alternatively, use an arrow function that inherits this lexically.

const obj = {
  prefix: '>',
  log(arr) {
    arr.forEach(function (x) { console.log(this.prefix, x) }, this) // thisArg
    arr.forEach(x => console.log(this.prefix, x))                   // arrow
  },
}

Without the thisArg (or an arrow), a regular callback's this would be undefined/global, and this.prefix would throw. Arrows are the modern, preferred fix.

Method borrowing uses call/apply to invoke a method from one object on another object that doesn't have it — by explicitly setting this. Classic for array-like objects.

function sum() {
  // `arguments` is array-like, not a real array
  return Array.prototype.reduce.call(arguments, (a, b) => a + b, 0)
}
sum(1, 2, 3) // 6

const arrayLike = { 0: 'a', 1: 'b', length: 2 }
Array.prototype.join.call(arrayLike, '-') // 'a-b'

You "borrow" Array.prototype methods for array-likes (arguments, NodeLists). Modern code often uses Array.from/spread instead, but borrowing shows how this decouples a method from its object.

Beyond setting this, bind lets you pre-fill leading arguments, returning a function that takes the rest — partial application.

function multiply(a, b) { return a * b }
const double = multiply.bind(null, 2) // a is fixed to 2
double(5) // 10

const log = console.log.bind(console, '[APP]')
log('started') // [APP] started

The null thisArg is common when you only care about pre-binding arguments, not this. The bound arguments are remembered via the bound function's internal state.

Yes. new takes precedence over bind — when you call new on a bound function, this is the new instance, not the value you bound. (Pre-bound arguments are still applied, though.)

function Point(x, y) { this.x = x; this.y = y }
const BoundPoint = Point.bind({ ignored: true }, 10)
const p = new BoundPoint(20)
p.x // 10 (bound arg kept)
p.y // 20
// p is a fresh instance; the bound { ignored: true } this was ignored

This is the one case where bind's "permanent" this is overridden, reflecting that new is the highest-precedence binding rule.

Passing obj.method as a callback copies only the function, detaching it from obj. When the receiver later calls it as a plain function, implicit binding is gone, so this is undefined/global.

const user = { name: 'Ada', greet() { return this.name } }
const fn = user.greet
fn()                       // undefined — detached
setTimeout(user.greet, 0)  // also detached

setTimeout(() => user.greet(), 0)     // called ON user
setTimeout(user.greet.bind(user), 0)  // bound

The fix is to preserve the receiver via an arrow wrapper or bind.

A regular-function .then callback is invoked by the promise machinery with no receiver, so this is undefined (strict) / global. An arrow .then callback inherits this from the enclosing scope.

class Loader {
  url = '/api'
  load() {
    fetch(this.url)
      .then(r => r.json())
      .then(data => this.handle(data)) // arrow keeps `this` = the Loader
  }
  handle(data) {}
}

Using arrows in .then chains is the norm precisely so this stays the surrounding object. A regular function there would lose it.

Inside a class method, this refers to the instance when called as instance.method(). But class bodies are always strict, so an extracted method called plainly has this === undefined (not the global object).

class Counter {
  count = 0
  inc() { this.count++ }
}
const c = new Counter()
const f = c.inc
f() // TypeError: Cannot read properties of undefined

// fix with a class field arrow method:
class Counter2 { count = 0; inc = () => this.count++ }

Arrow class fields bind this to the instance permanently, which is why they're popular for handlers.

It depends on the context. In a classic script (non-strict), top-level this is the global object (window). In an ES module, top-level this is undefined (modules are always strict and have their own scope). In Node CommonScript modules, it's module.exports.

// classic <script>:  this === window
// ES module (type="module" / .mjs):  this === undefined
// Node CommonScript module:  this === module.exports

Use globalThis to reliably reference the global object across all of these environments.

An arrow method captures this from the enclosing scope at definition time — which for an object literal is the surrounding scope, not the object. So this won't be the object you expect.

const obj = {
  name: 'Ada',
  greet: () => `Hi, ${this.name}`, // `this` is the outer scope, not obj
}
obj.greet() // 'Hi, undefined'

const obj2 = { name: 'Ada', greet() { return `Hi, ${this.name}` } }
obj2.greet() // 'Hi, Ada'

Use method shorthand (a regular function) for object methods that reference this. Reserve arrows for callbacks where you want the outer this.

globalThis (ES2020) is a standardized way to access the global object in any JavaScript environment — window in browsers, self in workers, global in Node — without environment checks.

globalThis.myGlobal = 42
// before globalThis you needed:
const g = typeof window !== 'undefined' ? window
        : typeof global !== 'undefined' ? global : self

It removes the brittle feature-detection people used to write. Relevant to this because default-bound this in sloppy mode is this global object.

Destructuring extracts the function value without its object, so calling it loses implicit binding — this becomes undefined/global, exactly like assigning the method to a variable.

const { greet } = user
greet() // `this` is not `user` -> likely a TypeError

// common real example: destructuring instance methods
const { increment } = counter
increment() // detached

If a method uses this, don't destructure it (or bind it first). This bites people destructuring class instances or React class methods.

A regular-function setTimeout callback is invoked by the timer with no receiver, so this is the global object (sloppy) / undefined (strict). An arrow callback inherits this from the surrounding scope.

const obj = {
  value: 42,
  start() {
    setTimeout(function () { console.log(this.value) }, 100) // undefined/global
    setTimeout(() => console.log(this.value), 100)           // 42 (arrow)
  },
}

The arrow form (or bind(this)) is the standard way to keep the surrounding object's this in timer callbacks.

In a getter/setter, this is the object the property is accessed on — just like a regular method — so you can compute the value from other properties of that object.

const person = {
  first: 'Ada', last: 'Lovelace',
  get fullName() { return `${this.first} ${this.last}` },
  set fullName(v) { [this.first, this.last] = v.split(' ') },
}
person.fullName        // 'Ada Lovelace'
person.fullName = 'Grace Hopper'

Don't define a getter/setter with an arrow — it wouldn't bind this to the object. Use regular get/set syntax.

In a static method, this refers to the class itself (the constructor function), not an instance — so you can access other static members.

class MathUtil {
  static PI = 3.14159
  static circleArea(r) { return this.PI * r * r } // this === MathUtil
}
MathUtil.circleArea(2) // 12.566...

Because this is the class, static methods can call sibling statics via this, and this even respects subclasses (if called as SubClass.method(), this is SubClass).

Before arrow functions, you'd capture the outer this into an ordinary variable (self, that, or _this) so nested regular functions could reference it via the closure, sidestepping their own this.

function Timer() {
  const self = this
  this.seconds = 0
  setInterval(function () {
    self.seconds++ // uses the captured outer `this`
  }, 1000)
}

Arrow functions made this obsolete — setInterval(() => this.seconds++, 1000) does the same thing — but you'll still see self/that in older codebases and transpiled output.

A class method passed as an event handler (onClick={this.handleClick}) is detached from the instance, so this is undefined when React calls it. Class bodies are strict, so you get a TypeError on this.setState.

class Btn extends React.Component {
  constructor(p) { super(p); this.handleClick = this.handleClick.bind(this) }
  handleClick() { this.setState(...) } // needs bound `this`
  // or: handleClick = () => this.setState(...)  // arrow class field
}

Fixes: bind in the constructor, or use an arrow class field (which captures the instance this). Function components with hooks avoid the issue entirely.

If each method returns this (the instance), you can chain calls together — the basis of fluent/builder APIs.

class Query {
  parts = []
  where(c) { this.parts.push(c); return this }
  order(o) { this.parts.push(o); return this }
  build() { return this.parts.join(' ') }
}
new Query().where('a=1').order('b').build()

Each call resolves this to the same instance (implicit binding) and hands it back, so the next call operates on the same object. Returning this is the key.

A regular function defined inside a method does not inherit the method's this. When called plainly, it gets its own default binding (undefined/global), which is a frequent bug.

const obj = {
  items: [1, 2],
  process() {
    this.items.forEach(function (x) {
      console.log(this.items) // `this` is not obj here
    })
  },
}

Fixes: arrow function (inherits this), thisArg, or const self = this. The nested regular function losing this is the classic gotcha arrows solved.

apply was historically used to pass an array of arguments and set this. The spread operator (...) spreads an array into arguments without dealing with this, so it's cleaner when you only need to forward arguments.

const nums = [5, 6, 2, 3]
Math.max.apply(null, nums) // 6 (old way — null thisArg)
Math.max(...nums)          // 6 (modern, no thisArg needed)

Use spread when this is irrelevant (most cases); keep apply when you must set this and pass an arguments array together.

An IIFE is invoked as a plain function call, so default binding applies: this is the global object in sloppy mode and undefined in strict mode.

(function () {
  console.log(this) // window (sloppy) / undefined (strict)
})()

(function () {
  'use strict'
  console.log(this) // undefined
})()

That's why old module-pattern IIFEs sometimes passed this/window in explicitly: (function (global) { ... })(this).

Method shorthand (method() {}) creates a regular function, so this is the object the method is called on — standard implicit binding.

const calculator = {
  value: 0,
  add(n) { this.value += n; return this },
}
calculator.add(5).add(3) // this is calculator throughout -> value 8

Shorthand is the right way to define methods that use this. (Contrast with an arrow property, which would NOT bind to the object.)

No. Once a function is bound with bind, its this is permanently fixed — calling bind again, or call/apply with a different this, cannot change it.

function f() { return this.id }
const bound = f.bind({ id: 1 })
bound.call({ id: 2 }) // 1 — the rebind is ignored
bound.bind({ id: 3 })() // 1 — still 1

The first bind wins. This "hard binding" is sometimes the goal (locking this), but it surprises people expecting call to override it.

In sloppy mode, a primitive thisArg passed to call/apply/bind is boxed into its wrapper object (Number, String, Boolean), and null/ undefined become the global object. In strict mode, this is left exactly as passed.

function f() { return this }
// sloppy mode:
f.call(5)         // Number {5}  (boxed)
f.call(null)      // global object
// strict mode:
f.call(5)         // 5 (primitive, unboxed)
f.call(null)      // null

Strict mode's "leave this as-is" behavior is more predictable, another reason modern code runs strict by default.

Arrow functions also lack their own arguments object, super, and new.target, and they can't be used as constructors (no new). Like this, arguments is inherited lexically from the enclosing function.

function outer() {
  const arrow = () => arguments[0] // refers to outer's arguments
  return arrow
}
outer(42)() // 42

const Foo = () => {}
new Foo() // TypeError: Foo is not a constructor

Use rest parameters (...args) instead of arguments in arrows. These omissions are why arrows are lightweight callbacks, not general-purpose functions.

A method on a constructor's prototype behaves like any method: when called as instance.method(), this is the instance. The method lives once on the prototype but this resolves per call to whichever instance invoked it.

function User(name) { this.name = name }
User.prototype.greet = function () { return `Hi, ${this.name}` }
const u = new User('Ada')
u.greet() // 'Hi, Ada' — this === u

This is how one shared prototype method serves all instances correctly — this is determined by the call site, not where the method is defined.

Practice tests are coming soon

Get notified when interactive mock interviews and quizzes launch.