Lost `this` in callbacks: why static methods throw “undefined is not an object”
When you pass a class method into Array.prototype.map, JavaScript calls it without a receiver—so inside that method, `this` is not your class. This piece explains the binding rules, shows a standalone inventory-formatting example, and lists practical fixes.
The error that looks like a missing function
In strict browser and bundler environments, you might see something like:
TypeError: undefined is not an object (evaluating 'this.normalizeUnit')
The stack trace points at a line inside a static method—often one that calls another helper on the same class using this.helper(...). Nothing is wrong with the helper’s definition, yet at runtime this is not what you expect.
This post explains why that happens, using a small, self-contained example unrelated to any particular app.
What JavaScript actually does with this
this is not resolved where a function is written. For ordinary functions, it is largely resolved how the function is called:
obj.method()→ insidemethod,thisis typicallyobj.method()→ in strict mode,thisisundefined(for a plain call).arr.map(method)→ whenmapinvokes your callback, it callsmethod(element)with no receiver—sothisis lost unless something else fixes it.
That last case is the heart of the bug.
A neutral example: formatting inventory rows
Imagine a utility class that turns raw warehouse records into display strings:
class InventoryRowFormatter {
static unitLabel(unit) {
return unit === "kg" ? "kilograms" : unit;
}
static describeLine(row) {
const label = this.unitLabel(row.unit);
return `${row.name}: ${row.qty} ${label}`;
}
static describeAll(rows) {
return rows.map(InventoryRowFormatter.describeLine);
}
}
describeAll looks reasonable: it delegates to map with InventoryRowFormatter.describeLine. Inside describeLine, the author used this.unitLabel so both methods “live together” on the class.
Run it:
const rows = [
{ name: "Bolts", qty: 40, unit: "kg" },
{ name: "Washers", qty: 100, unit: "pcs" },
];
console.log(InventoryRowFormatter.describeAll(rows));
You get the TypeError about this.unitLabel (or similar), because:
Array.prototype.mapcalls the callback ascallback(currentValue, index, array).- That is a direct call—no
InventoryRowFormatteron the left of the dot. - Inside
describeLine,thisis thereforeundefinedin strict mode. - Reading
this.unitLabeltries to read a property fromundefined→ runtime error.
So the function exists; the binding of this does not.
Why static methods make this especially confusing
Developers often think of static methods as “functions that live on the constructor object.” That is true, but static methods are still just functions. When you pass InventoryRowFormatter.describeLine as a value, you are passing the function without attaching the class as this.
Inside a static method, this refers to the constructor (InventoryRowFormatter) only when the runtime invoked the static method with that receiver—e.g. InventoryRowFormatter.describeLine(row). When map calls it, the receiver is gone.
Using this to reach another static helper is therefore fragile whenever that method might be used as a callback or higher-order argument.
Ways to fix it (pick one style and stay consistent)
1. Call the class explicitly (simplest for static-only helpers)
static describeLine(row) {
const label = InventoryRowFormatter.unitLabel(row.unit);
return `${row.name}: ${row.qty} ${label}`;
}
No reliance on this—the behavior is identical whether describeLine is called directly or passed to map.
2. Wrap at the call site
static describeAll(rows) {
return rows.map((row) => InventoryRowFormatter.describeLine(row));
}
Here each invocation uses the normal static call form, so if describeLine still used this, it would work—but wrapping is redundant if you already fixed describeLine to avoid this.
3. Bind the function
rows.map(InventoryRowFormatter.describeLine.bind(InventoryRowFormatter))
This permanently ties this inside describeLine to the class. It works, but it is easy to forget and slightly noisy.
4. Instance methods vs static
If you used instance methods and did obj.describeLine, this would be obj. That is a different design. For pure mapping utilities, explicit class-name calls or small lambdas are usually clearer than instance state.
Mental model to keep
- Passing
ClassName.methodis passing a bare function—do not assumethiswill beClassName. thisinside static methods is only the class when called asClassName.method(...)(orthis.methodfrom another static method only if the outer call still had the rightthis—which breaks once you pass the inner one as a callback).- Prefer
ClassName.otherStatic(...)overthis.otherStatic(...)inside static utilities that might become callbacks.
Summary
The error is not “the helper is missing from the class.” It is that this was undefined because the method ran as a detached callback. Fixing it means restoring an explicit receiver (wrap, bind) or not using this for sibling static helpers (call the class by name). Once you recognize the pattern, it shows up everywhere: event handlers, setTimeout, Array methods, and framework callbacks—all places where the way the function is invoked, not where it is declared, decides what this means.
Rate this post
All fields are optional. Just stars is fine.