Soal 1Apa yang terjadi kalau kamu memanggil new pada EBook yang meng-assign this.fileSize sebelum super()?
Inheritance — extends dan super
extends dipakai untuk membuat class baru di atas class yang sudah ada. Membahas super() di constructor, override, dan super.methodName().
Misalnya sebuah toko buku online butuh class untuk e-book, di samping class Book untuk buku cetak. Bedanya hanya dua: e-book bisa diunduh dan tidak kena ongkos kirim. Tapi dengan cara yang kamu tahu sejauh ini, bagian yang mengurus judul dan harga pun harus ditulis ulang.
Artikel ini membahas extends, yang membangun class baru di atas class yang sudah ada, dan super, yang memanggil kode di class yang diwarisi itu.
Memakai Ulang Kode Tanpa Menyalinnya — extends
E-book juga butuh constructor yang menerima judul dan harga, serta method describe yang mengembalikan "judul: harga yen". Kalau isi Book kamu salin ke class e-book yang baru, baris yang sama ada di dua tempat. Kalau nanti format tampilannya diubah dan salah satu salinan lupa diperbarui, tampilan di tiap layar jadi berbeda.
Inheritance (mekanisme agar class lain bisa memakai constructor dan method dari class yang sudah ada) ditulis sebagai class EBook extends Book. Book disebut parent class, dan EBook disebut child class.
extends Error yang kamu tulis di artikel throw dan custom error juga termasuk inheritance.
class Book {
constructor(title, price) {
this.title = title;
this.price = price;
}
describe() { return `${this.title}: ${this.price} yen`; }
}
// Mewarisi Book dan menambahkan method yang hanya dimiliki e-book
class EBook extends Book {
download() { return `Mengunduh ${this.title}`; }
}
const ebook = new EBook("Statistika Dasar", 2200); // Argument-nya diteruskan ke constructor Book
console.log(ebook.describe()); // Statistika Dasar: 2200 yen
console.log(ebook.download()); // Mengunduh Statistika Dasar
const book = new Book("Statistika Dasar", 2640);
console.log(typeof book.download); // undefined (parent class tidak punya method milik child class)
Di dalam ebook.describe(), this adalah ebook, jadi this.title yang ditulis di Book membaca judul yang kamu berikan ke new EBook. Yang memasukkan judul itu ke ebook pun adalah constructor milik Book, yang menerima argument-nya.
Menambah Value di Child Class — super() dan constructor
Sekarang e-book juga perlu menyimpan ukuran file, fileSize, selain judul dan harga. Untuk menerima ukuran file, EBook butuh constructor sendiri. Tapi begitu kamu menulisnya, constructor milik Book tidak lagi dipanggil otomatis, dan kalau dibiarkan begitu saja, new akan berhenti dengan ReferenceError.
super() (cara memanggil constructor parent class dari dalam constructor child class) menerima argument yang diteruskan ke parent, seperti super(title, price);.
Di child class, this disiapkan oleh constructor parent selama super() berjalan, jadi panggil super() sebelum baris mana pun yang memakai this.
class Book {
constructor(title, price) {
this.title = title;
this.price = price;
}
}
class EBook extends Book {
constructor(title, price, fileSize) {
// Kalau this.fileSize = fileSize; ditulis sebelum baris ini, new melempar ReferenceError
super(title, price); // Constructor Book mengisi title dan price
this.fileSize = fileSize; // Setelah itu, tambahkan value yang hanya dimiliki EBook
}
}
const ebook = new EBook("Statistika Dasar", 2200, 12);
console.log(ebook.title); // Statistika Dasar
console.log(ebook.fileSize); // 12
console.log(Object.keys(ebook).join(", ")); // title, price, fileSize
Key-nya muncul dengan urutan title, price, fileSize karena Book lebih dulu mengisi value-nya di dalam super(). Baris atas gagal dengan ReferenceError: Must call super constructor in derived class before accessing 'this' …, dan "derived class" di sini maksudnya child class.
Child Class Tidak Bisa Membaca Field # Milik Parent
Kalau Book punya private field #price, super() tetap mengisi value-nya, tapi menulis this.#price di body EBook melempar SyntaxError. Nama # hanya bisa dipakai di dalam class yang mendeklarasikannya, jadi child class harus membacanya lewat getter yang didefinisikan di parent.
Mengubah Ongkos Kirim di Child Class — Override
Kalau kamu menambahkan method shippingFee ke Book, e-book juga kena ongkos kirim yang sama dengan buku cetak. Kamu bisa saja membuatnya 0 yen khusus untuk e-book dengan memeriksa jenis buku memakai if di dalam Book, tapi percabangan di parent class akan bertambah setiap kali ada child class baru.
Override (menulis method di child class dengan nama yang sama seperti di parent, sehingga versi child yang berjalan untuk instance child) cukup dilakukan dengan menulis ulang shippingFee di body EBook. Saat method dipanggil, JavaScript mencari namanya mulai dari class yang membuat instance itu, lalu lanjut ke parent class kalau tidak ditemukan.
class Book {
constructor(title, price) {
this.title = title;
this.price = price;
}
describe() { return `${this.title}: ${this.price} yen`; }
shippingFee() { return this.price >= 3000 ? 0 : 500; } // Gratis ongkir mulai 3000 yen
}
class EBook extends Book {
shippingFee() { return 0; } // Di-override dengan nama yang sama (tidak ada barang yang dikirim)
}
const book = new Book("Statistika Dasar", 2640);
const ebook = new EBook("Statistika Dasar", 2200);
console.log(book.shippingFee()); // 500
console.log(ebook.shippingFee()); // 0 (shippingFee milik EBook yang berjalan)
console.log(ebook.describe()); // Statistika Dasar: 2200 yen (describe milik Book yang berjalan)
shippingFee milik EBook tidak memakai perhitungan Book, jadi walaupun aturan ongkos kirim di Book diubah, e-book tetap 0 yen. Bagian berikutnya membahas cara memanggil kode parent lalu menambahkan bagian yang berbeda saja.
Menambahkan ke Kode Parent — super.methodName()
Berikutnya, kamu ingin deskripsi e-book diakhiri dengan "(e-book, 12 MB)". Kalau kamu meng-override describe di EBook dan ikut menyalin kode yang menyusun "judul: harga yen", begitu format tampilan Book diubah, deskripsi e-book akan tetap memakai format lama.
Dengan super.methodName() (cara memanggil method yang didefinisikan di parent class dari dalam method child class), super.describe() mengembalikan return value describe milik Book. Child class cukup menambahkan detail e-book ke string itu.
class Book {
constructor(title, price) {
this.title = title;
this.price = price;
}
describe() { return `${this.title}: ${this.price} yen`; }
}
class EBook extends Book {
constructor(title, price, fileSize) {
super(title, price);
this.fileSize = fileSize;
}
// Menambahkan detail e-book ke return value describe milik Book
describe() { return `${super.describe()} (e-book, ${this.fileSize} MB)`; }
}
const ebook = new EBook("Statistika Dasar", 2200, 12);
console.log(ebook.describe()); // Statistika Dasar: 2200 yen (e-book, 12 MB)
super.describe() adalah ekspresi yang menghasilkan return value Book, jadi kalau kamu ingin detail e-book ada di depan, cukup pindahkan posisinya di dalam template literal. Tabel di bawah merangkum sintaks inheritance yang dipakai di artikel ini dan apa yang berjalan untuk masing-masing.
| Sintaks | Tempat menulis | Yang berjalan |
|---|---|---|
| class EBook extends Book | Deklarasi class | Mewarisi constructor dan method Book |
| super(title, price) | Constructor child, sebelum this | Constructor Book |
| Method bernama sama dengan parent | Body child class | Versi EBook, untuk instance EBook |
| super.describe() | Di dalam method child | describe milik Book |
Tanpa super, Method Terus Memanggil Dirinya Sendiri
Kalau kamu menulis this.describe(), bukan super.describe(), di describe milik child class, method itu terus memanggil dirinya sendiri sampai berhenti dengan RangeError: Maximum call stack size exceeded. Untuk memanggil kode parent dari method yang sudah di-override, pakai super..
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Kalau Book dan EBook sama-sama punya method shippingFee, mana yang berjalan saat kamu memanggil ebook.shippingFee()?
Soal 3Di dalam describe milik EBook, bagaimana cara mendapatkan return value describe milik Book?