Belajar Dasar JavaScript: Pewarisan/Inheritance Kelas

Pewarisan (Inheritance) memungkinkan kita membuat kelas yang mewarisi anggota dari kelas lain. Kelas pewaris disebut kelas induk atau super class. Kelas yang mewarisi disebut kelas turunan atau sub class. Pewarisan merupakan fitur penting pada pemrograman OOP.

Dasar

Dalam bahasa JavaScript inheritance bisa dilakukan dengan keyword extends. Contohnya:

class Sepeda {
  nama = "Sepeda";

  hidupkan() {
    console.log("Mesin hidupkan");
  }

  matikan() {
    console.log("Mesin dimatikan");
  }

  matikanLampu() {
    console.log("Lampu dihidupkan");
  }

  hidupkanLampu() {
    console.log("Lampu dimatikan");
  }
}

class Beat extends Sepeda {}

Pada contoh di atas kita membuat kelas bernama Sepeda dan Beat. Kelas Beat mewarisi kelas Sepeda. Jadi, kelas Beat adalah kelas turunan dan kelas Sepeda adalah kelas induk. Kelas Beat akan memiliki semua properti dan fungsi yang dimiliki kelas Sepeda.

Contohnya penggunaan kelas Beat:

const sepedaBeat = new Beat();
console.log(sepedaBeat.nama); // Sepeda
sepedaBeat.hidupkan(); // Mesin hidupkan
sepedaBeat.hidupkanLampu(); // Lampu dihidupkan
sepedaBeat.matikanLampu(); // Lampu dimatikan
sepedaBeat.matikan(); // Mesin dimatikan

Override Properti dan Fungsi

Override memungkinkan kita mengimplementasikan (mendeklarasikan) ulang fungsi atau properti pada kelas induk padahal fungsi/properti tersebut sudah ada di kelas induk. Contohnya:

class Beat extends Sepeda {
  nama = "Beat";

  hidupkan() {
    super.hidupkan();
    console.log(`Mesin ${this.nama} dimulai`);
  }

  matikan() {
    console.log(`Mesin ${this.nama} dimatikan`);
    super.matikan();
  }

  matikanLampu() {
    console.log("Lampu tidak bisa dimatikan! Harus hidup");
  }

  hidupkanLampu() {
    console.log("Lampu sudah dan jelas hidup!");
  }
}

Pada contoh di atas kita mengoverride fungsi hidupkan, matikan, hidupkanLampu dan matikanLampu. Saat keempat fungsi tersebut dipanggil maka yang dipanggil adalah fungsi dari kelas anak turunan kelas induk. Kita juga mendeklarasikan ulang (override) properti nama.

super.hidupkan() akan memanggil fungsi hidupkan dari kelas Sepeda. super.matikan() akan memanggil fungsi matikan dari kelas Sepeda. Jadi, keyword super digunakan untuk memanggil fungsi dari kelas induk.

Saat kedua fungsi kita panggil akan muncul seperti ini:

const sepedaBeat = new Beat();
console.log(sepedaBeat.nama); // Beat
sepedaBeat.hidupkan();
// Mesin hidupkan
// Mesin Beat dimulai
sepedaBeat.hidupkanLampu(); // Lampu sudah dan jelas hidup!
sepedaBeat.matikanLampu(); // Lampu tidak bisa dimatikan! Harus hidup
sepedaBeat.matikan();
// Mesin Beat dimatikan
// Mesin dimatikan

Pada fungsi hidupkan dan matikan, fungsi kelas induk dipanggil karena kita memanggilnya menggunakan super. Pada fungsi hidupkanLampu dan matikanLampu, fungsi kelas induk tidak terpanggil. Kenapa? Karena kita tidak memanggilnya menggunakan super.

Saat mengoverride fungsi, memanggil fungsi induk dengan super itu tidak wajib.

Override Constructor

Untuk mengoverride constructor, caranya hampir sama dengan mengoverride fungsi. Contohnya seperti berikut:

class Mobil {
  roda = 4;
  constructor() {
    console.log("Mobil dibuat");
  }
}

class Toyota extends Mobil {
  constructor() {
    console.log("Membuat mobil Toyota");
    console.log(`Mobil punya ${this.roda} `);
  }
}

const mobil = new Toyota();
/*
Membuat mobil Toyota
Uncaught:
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    at new Toyota (REPL13:4:17)
*/

Kenapa terjadi error? Jadi saat mengoverride constuctor, sebelum mengakses this kita harus memanggil konstruktor kelas induk dengan super. Agar tidak terjadi error, maka kode dirubah seperti berikut:

class Toyota extends Mobil {
  constructor() {
    super();
    console.log("Membuat mobil Toyota");
    console.log(`Mobil punya ${this.roda} `);
  }
}

const mobil = new Toyota();
/*
Mobil dibuat
Membuat mobil Toyota
Mobil punya 4
*/

Anggota Privat

Kelas turunan tidak dapat mengakses anggota privat dari kelas induk. Contohnya seperti ini:

class Laptop {
  #ram = 8;
  #getTemp() {
    return 20;
  }
}
class Asus extends Laptop {
  getRam() {
    return super.#ram;
  }
  getTemp() {
    return super.#getTemp();
  }
}

const laptop = new Asus()
// Uncaught SyntaxError: Unexpected private field
console.log(laptop.getRam())
console.log(laptop.getTemp())

Ternyata saat kita membuat objek dari kelas Asus saja sudah terjadi error karena kita mengakses anggota privat dari kelas induk.