Logo
Published on

Super Calls

How Super Calls Improves Code Quality

Understanding super() calls in inheritance enables developers to properly extend parent classes while maintaining initialization chains. This technique ensures parent constructors run correctly while allowing child customization, making it essential for modern JavaScript development. Teams using proper super() patterns report cleaner inheritance and fewer initialization bugs.

TL;DR

  • Call super() before using this
  • Super Calls works seamlessly with extends
  • Passes parameters to parent constructors
  • Perfect for class hierarchies and inheritance
class Car extends Vehicle {
  constructor(brand) {
    super(brand)
  }
}

The Super Calls Challenge

You're reviewing code where child classes try to replicate parent initialization logic instead of calling super(). This duplication leads to inconsistencies when parent classes change, and initialization order bugs when accessing this before super().

// The problematic approach - no super call
class Vehicle {
  constructor(brand) {
    this.brand = brand
    this.id = Date.now()
  }
}
class BadCar {
  constructor(brand, doors) {
    this.brand = brand // Duplicating parent logic
    this.doors = doors
    // Missing parent ID logic!
  }
}
const badCar = new BadCar('Honda', 4)
console.log('Missing ID:', badCar.id) // undefined!

Modern super() calls properly chain constructors and ensure parent initialization happens correctly:

// The elegant solution with super()
class Vehicle {
  constructor(brand, model) {
    this.brand = brand
    this.model = model
    this.id = Date.now()
  }
}
class GoodCar extends Vehicle {
  constructor(brand, model, doors) {
    super(brand, model) // Call parent first
    this.doors = doors
  }
}
const car2 = new GoodCar('Toyota', 'Camry', 4)
console.log('Has ID:', car2.id) // Works!

Best Practises

Use super calls when:

  • ✅ Extending any parent class with extends
  • ✅ Child needs parent's initialization logic
  • ✅ Accessing parent methods via super.method()
  • ✅ Building class hierarchies with shared behavior

Avoid when:

  • 🚩 Not using class inheritance at all
  • 🚩 Composition would be better than inheritance
  • 🚩 Parent constructor has side effects
  • 🚩 Deep inheritance chains (prefer composition)

System Design Trade-offs

AspectSuper() InheritanceManual Prototype
SyntaxClean - super() keywordComplex - .call(this)
Order SafetyEnforced - super firstError-prone - manual
Parent AccessEasy - super.method()Verbose - Parent.prototype
RefactoringSimple - change parentHard - update all children
Type SafetyGood - instanceof worksFragile - manual chain
Browser SupportES6+ requiredAll browsers

More Code Examples

❌ Prototype chain mess
// Traditional prototype inheritance without super
function Animal(name, species) {
  this.name = name
  this.species = species
  this.id = 'ANIMAL-' + Date.now()
  this.createdAt = new Date()
}
Animal.prototype.speak = function () {
  return this.name + ' makes a sound'
}
Animal.prototype.move = function () {
  return this.name + ' moves'
}
// Manual prototype chain setup
function Dog(name, breed) {
  // Manual parent call - error prone
  Animal.call(this, name, 'Canine')
  this.breed = breed
  // Forgetting parent init causes bugs
}
// Complex prototype linkage
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog
Dog.prototype.bark = function () {
  return this.name + ' barks'
}
// Override parent method manually
Dog.prototype.speak = function () {
  // Calling parent method is verbose
  return Animal.prototype.speak.call(this) + ' (woof!)'
}
// Multi-level inheritance nightmare
function Puppy(name, breed, age) {
  Dog.call(this, name, breed)
  this.age = age
  // Easy to forget parent initialization
}
Puppy.prototype = Object.create(Dog.prototype)
Puppy.prototype.constructor = Puppy
// Testing the chain
const puppy = new Puppy('Max', 'Golden', 1)
console.log('Puppy speaks:', puppy.speak())
console.log('Has ID?', puppy.id)
// Prototype chain is fragile
console.log('Is Animal?', puppy instanceof Animal)
console.log('Is Dog?', puppy instanceof Dog)
✅ Super calls chain properly
// Modern ES6 classes with super()
class Animal {
  constructor(name, species) {
    this.name = name
    this.species = species
    this.id = `ANIMAL-${Date.now()}`
    this.createdAt = new Date()
  }
  speak() {
    return `${this.name} makes a sound`
  }
  move() {
    return `${this.name} moves`
  }
}
// Clean inheritance with extends
class Dog extends Animal {
  constructor(name, breed) {
    // super() must come first
    super(name, 'Canine')
    this.breed = breed
    console.log('Dog initialized with super()')
  }
  bark() {
    return `${this.name} barks`
  }
  // Override with super access
  speak() {
    return `${super.speak()} (woof!)`
  }
}
// Multi-level inheritance is clean
class Puppy extends Dog {
  constructor(name, breed, age) {
    super(name, breed)
    this.age = age
    this.playful = true
  }
  play() {
    return `${this.name} wants to play!`
  }
  // Access grandparent via super chain
  move() {
    return `${super.move()} playfully`
  }
}
// Everything just works
const puppy = new Puppy('Max', 'Golden', 1)
console.log('Puppy speaks:', puppy.speak())
console.log('Has ID?', puppy.id)
console.log('Created:', puppy.createdAt)
// instanceof works correctly
console.log('Is Animal?', puppy instanceof Animal)
console.log('Is Dog?', puppy instanceof Dog)
console.log('Is Puppy?', puppy instanceof Puppy)
// Method resolution works
console.log('Plays:', puppy.play())
console.log('Moves:', puppy.move())

Technical Trivia

The Super Calls Bug of 2018: A major framework's component library crashed when developers accessed 'this' before calling super() in extended components. The bug caused ReferenceErrors in production, breaking thousands of websites that used the library.

Why the pattern failed: JavaScript requires super() to be called before accessing 'this' in derived constructors. The library's BaseComponent was setting this.props before super(), which worked in development but failed when minified, as the minifier reordered the statements.

Modern tooling prevents these issues: Today's JavaScript engines throw immediate ReferenceErrors when accessing 'this' before super(). ESLint's no-this-before-super rule catches these errors at development time. TypeScript enforces super() calls in derived constructors.


Master Super Calls: Implementation Strategy

Always call super() as the first statement in derived constructors to ensure proper initialization order. The extends/super pattern provides clean inheritance that's easier to understand and maintain than prototype manipulation. Use super.method() to access parent functionality, but keep inheritance shallow - prefer composition over deep hierarchies.