this/super Used Before super() in Derived Constructor
ID |
javascript.no_this_before_super |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
JavaScript |
Tags |
reliability, suspicious-control-flow |
Description
Reports any reference to this or super.foo inside a derived-class constructor (a class with an extends clause) that appears textually before the constructor’s first super(…) call. ECMAScript binds this in a derived constructor only after super(…) returns; reading or writing this (or accessing super.foo) before that point throws a ReferenceError at runtime: "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
This is a v1 implementation that uses textual position rather than full control-flow analysis. References inside a nested non-arrow function are skipped because they do not run synchronously with the constructor body. References inside arrow functions are still considered, because arrows do not bind their own this or super.
If the constructor has no super(…) call at all, this rule does not fire — the sister rule constructor_super covers that case.
Rationale
-
Pre-
super()access tothisis an immediateReferenceErrorin modern engines. The error is masked when the failing branch is rarely exercised in tests. -
super.method()beforesuper()reads from the prototype chain through the not-yet-initialized derived instance and is equally rejected by the engine. -
Even if the runtime semantics were lenient, the order is so surprising for readers that putting
super(…)first is the only sensible style.
// Bad
class Greeter extends Base {
constructor(name) {
this.name = name; // ReferenceError before super()
super();
}
}
class Wrapper extends Base {
constructor() {
super.init(); // accessing super before super()
super();
}
}
Remediation
Place super(…) at the top of the constructor body, before any reference to this or super.
// Good
class Greeter extends Base {
constructor(name) {
super();
this.name = name;
}
}
If you need to compute an argument for super(…) from constructor parameters, do so with local variables — not via this:
// Good
class Tagged extends Base {
constructor(arg) {
const tag = arg ? String(arg) : "default";
super(tag);
this.tag = tag;
}
}