DEV3 Technologies home

DEV3 Developer Forum

How does `this` work in JavaScript and why is it undefined in my callback?

JavaScript helpSolved0 views
dev3 ·
class Counter {
  count = 0;
  inc() { this.count++; }
}
const c = new Counter();
button.addEventListener('click', c.inc);

Clicking gives Cannot read properties of undefined (reading 'count'). Why?

#javascript#this#functions

0

1 reply

dev3 · Accepted answer

this is decided by how a function is called, not where it is defined (except arrow functions).

The rules (in priority order)

  1. new Fn(): this is the new object.
  2. fn.call(obj), apply, bind(obj): this is obj.
  3. obj.method(): this is obj.
  4. Plain fn(): this is undefined in strict mode (modules and classes are strict), otherwise the global object.
  5. Arrow functions have no own this; they capture it from the surrounding scope.

Why your code fails

button.addEventListener('click', c.inc) passes the function by itself. When the browser calls it, it is a plain call with this set to the element (or undefined for class code), not c.

Fixes

// 1. bind
button.addEventListener('click', c.inc.bind(c));

// 2. wrapper arrow
button.addEventListener('click', () => c.inc());

// 3. arrow class field (each instance gets its own bound function)
class Counter {
  count = 0;
  inc = () => { this.count++; };
}

Other common cases

const o = { name: 'a', hi() { return this.name; } };
const f = o.hi;
f();            // undefined, lost the receiver

setTimeout(o.hi, 0);          // also lost
setTimeout(() => o.hi(), 0);  // fine

Do not use arrow functions as object methods when you need this to be the object.

Pitfall: bind creates a new function each call, so removeEventListener(type, c.inc.bind(c)) does not remove anything. Store the bound reference.

0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.