Belajar dengan membaca secara berurutan

Dasar class — Mendefinisikan Class dan Membuat Instance

Class dipakai untuk membuat banyak object dengan bentuk yang sama. Membahas cara membuat instance dengan constructor dan new, method yang memakai this, dan kapan object literal sudah cukup.

Member, produk, kupon: data yang punya field yang sama dan dipakai untuk perhitungan yang sama muncul berkali-kali di dalam aplikasi. Kalau setiap data kamu tulis sebagai object literal, field dan rumus perhitungannya ikut tersalin sebanyak jumlah datanya.

Artikel ini membahas class, yang menyatukan bentuk data dan kode pengolahnya di satu tempat, serta cara membuat object darinya satu per satu dengan new.

Menentukan Bentuk Data di Satu Tempat — class dan new

Ambil contoh dua jenis kupon diskon. Kalau { code: "WELCOME", discount: 500 } ditulis ulang untuk setiap kupon, lalu di salah satunya kamu salah ketik menjadi discont, kode tetap berjalan tanpa error, dan hanya value diskon kupon itu yang menjadi undefined. Nama key-nya ditulis di setiap kupon, jadi kamu harus mencari salah ketik itu di antara semuanya.

Class (sintaks untuk mendefinisikan jenis object yang dibuat dengan new) ditulis sebagai class Coupon { ... }, dan di dalamnya kamu menaruh constructor (bagian yang dipanggil oleh new) tanpa keyword function.

Di dalam constructor, this adalah instance baru (object yang dibuat dari class), dan new mengembalikan instance itu walaupun tidak ada return.

// Definisikan bentuk kupon di satu tempat dengan class
class Coupon {
  constructor(code, discount) {
    this.code = code;   // Kiri: code milik instance. Kanan: argument yang diterima
    this.discount = discount;
  }
}

// Buat kupon satu per satu dengan new
const welcome = new Coupon("WELCOME", 500);
const summer = new Coupon("SUMMER", 1000);
console.log(welcome.code);                // WELCOME
console.log(summer.discount);             // 1000
console.log(welcome instanceof Coupon);   // true

// Kalau dipanggil tanpa new, error dilempar
// Coupon("SPRING", 300);                 // TypeError
Hasil yang Berbeda dengan dan tanpa new
Definisiclass Couponnew Coupon("WELCOME", 500)new Coupon("SUMMER", 1000)Coupon("SPRING", 300)Isi this.codedengan "WELCOME"Isi this.codedengan "SUMMER"Tanpa new, tidakmasuk constructorwelcome berisiinstancesummer berisiinstance lainMelemparTypeErrordioperdioper
Argument ditulis ke this di dalam constructor. Tanpa new, error dilempar sebelum masuk ke constructor.

Nama key hanya ditulis di satu tempat, yaitu di dalam constructor, jadi salah ketik seperti discont cukup diperbaiki di satu tempat itu. Di kolom kanan, console menampilkan TypeError: Class constructor Coupon cannot be invoked without 'new'.

Buat data produk dengan class. productName dan productPrice sudah dideklarasikan.

① Definisikan Product agar setiap instance menyimpan nama produk dan harga.

② Buat mouse dengan memberikan kedua variabel itu.

③ Tampilkan mouse dalam format "Nama: harga yen".

④ Panggil Product tanpa new di dalam blok try, lalu tampilkan name dari error-nya.

(Kalau berjalan dengan benar, penjelasan akan muncul.)

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Menghitung dengan Value Milik Instance — Method dan this

Kalau rumus harga setelah diskon, total - welcome.discount, kamu tulis di halaman keranjang dan juga di halaman konfirmasi pesanan, setiap kali aturan harganya berubah ada dua tempat yang harus diperbaiki. Walaupun rumus itu dipindahkan ke sebuah function, field-nya tetap ada di dalam class, sedangkan perhitungannya ada di luar.

Method (function milik sebuah object yang dipanggil dengan titik, seperti welcome.apply(3000)) ditulis di dalam kurung kurawal class sebagai apply(total) { ... }, juga tanpa keyword function. Di dalam method, this merujuk ke instance yang ditulis di kiri titik, dan this.discount membaca value-nya pada saat method dipanggil.

class Coupon {
  constructor(code, discount) {
    this.code = code;
    this.discount = discount;
  }

  // Kurangi total dengan diskon kupon ini, lalu kembalikan hasilnya
  apply(total) {
    return total - this.discount;   // this adalah instance di kiri titik
  }
}

// Buat dua jenis kupon
const welcome = new Coupon("WELCOME", 500);
const summer = new Coupon("SUMMER", 1000);

// apply yang sama memberi hasil berbeda, tergantung instance di kiri titik
console.log(welcome.apply(3000));   // 2500
console.log(summer.apply(3000));    // 2000
Yang di Kiri Titik Menjadi this
welcome.apply(3000)this adalahwelcomethis.discountbernilai 500Mengembalikan2500summer.apply(3000)this adalahsummerthis.discountbernilai 1000Mengembalikan2000
this adalah variabel yang ditulis di kiri apply. Walaupun argument-nya sama, hasilnya berbeda kalau this-nya berbeda.
Jangkauan Argument constructor
Di dalam class Coupon
Di dalam constructor(code, discount)
  • Argument code / discount — hanya bisa dipakai di dalam kurung kurawal ini
  • this.discount = discount — menyimpan value di instance
Di dalam apply(total)
  • this.discount — membaca value yang disimpan di instance
  • total - discount — namanya tidak terlihat, jadi ReferenceError
Nama discount tidak terlihat di luar kurung kurawal constructor. apply membaca value yang disimpan di this.

Kalau kamu lupa menulis this. di dalam apply dan menulis total - discount, JavaScript mencari nama discount mulai dari kurung kurawal paling dalam ke arah luar. Jadi hasilnya bergantung pada ada atau tidaknya variabel dengan nama yang sama di luar class.

Lupa this. Tidak Selalu Menimbulkan Error

Kalau di luar class ada variabel dengan nama yang sama, misalnya const discount = 300;, total - discount yang lupa diberi this. tidak melempar ReferenceError. Kode itu diam-diam memakai 300 tersebut. Kalau hasilnya selalu sama, kupon apa pun yang dipakai, periksa apakah ada this. yang terlupa.

Hitung harga keyboard termasuk pajak dengan method milik Product. Product dan keyboard sudah dideklarasikan.

① Tambahkan method taxIncluded ke Product yang mengembalikan harga termasuk pajak (pajak 10%, dibulatkan ke bawah).

② Tampilkan nama keyboard dan harganya termasuk pajak dalam format "Nama: X yen (termasuk pajak)".

③ Ubah harga keyboard menjadi 9800.

④ Tampilkan lagi dalam format yang sama seperti ②.

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Setiap Instance Menyimpan Value Sendiri — Beberapa Instance

Setiap kali sebuah kupon dipakai, sisa kuotanya berkurang satu. Kalau sisa kuota disimpan di luar class dalam object seperti { WELCOME: 3, SUMMER: 3 }, setiap halaman yang memakai kupon butuh kode sendiri untuk mencari kuota berdasarkan code lalu menulis kembali value yang sudah dikurangi.

Di dalam method, kamu juga bisa meng-assign value ke property this, seperti this.remaining = this.remaining - 1. Sisi kanan membaca kuota saat ini, lalu hasilnya, yang sudah berkurang satu, ditulis kembali ke remaining milik instance yang sama. Kuotanya dimulai dari 3 yang kamu berikan ke new, lalu berkurang satu setiap kali method dipanggil.

class Coupon {
  constructor(code, remaining) {
    this.code = code;
    this.remaining = remaining;   // Sisa berapa kali bisa dipakai
  }

  // Kurangi kuota instance di kiri titik sebanyak satu
  use() {
    this.remaining = this.remaining - 1;
  }
}

// Buat dua kupon dengan kuota yang sama, lalu pakai dengan jumlah berbeda
const welcome = new Coupon("WELCOME", 3);
const summer = new Coupon("SUMMER", 3);
welcome.use();
welcome.use();
summer.use();
console.log(welcome.remaining);   // 1
console.log(summer.remaining);    // 2
use() Hanya Mengurangi Kuota di Kiri Titik
welcome.use()dua kalithis adalahwelcomeremaining3 → 2 → 1summer tidaktersentuhsummer.use()sekalithis adalahsummerremaining3 → 2welcome tidaktersentuh
use() milik welcome tidak menyentuh kuota summer, begitu juga sebaliknya. Yang berkurang hanya kuota instance yang ditulis di kiri use().

Instance tidak hanya bisa disimpan di variabel, tapi juga di array. Saat kamu memanggil method pada sebuah element, seperti coupons[0].use(), this merujuk ke element itu. Berapa pun jumlah kuponnya, kode yang mengurangi kuota tetap hanya satu baris di dalam use().

Kelola stok tiga produk di gudang dengan satu instance untuk setiap produk. Product, yang punya method ship untuk mengurangi stok, sudah dideklarasikan.

① Buat instance untuk 12 Monitor Arm, 30 USB Hub, dan 8 Stand Laptop, lalu masukkan ke sebuah array.

② Kirim 5 unit untuk produk pertama saja.

③ Tampilkan "Nama: stok X" untuk semua produk, satu per baris.

④ Tampilkan total stok dalam format "Total: X".

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Memilih Berdasarkan Jumlah Data — class atau Object Literal

Untuk data yang hanya dipakai sekali, seperti pengaturan tampilan sebuah halaman, cukup tulis { ... } di tempat itu. Tapi untuk data seperti kupon, yang dibuat berkali-kali dengan bentuk dan perhitungan yang sama, cara ini membuat isi perhitungannya pun ikut tersalin ke setiap data.

Object literal juga bisa punya method: tulis apply(total) { ... } di samping pasangan key dan value, dan this di dalamnya merujuk ke object itu. Bedanya dengan class ada di tempat body method ditulis, dan perbedaan itu terlihat di hasil Object.keys.

// class: body ditulis sekali, di dalam definisi
class Coupon {
  constructor(code, discount) {
    this.code = code;
    this.discount = discount;
  }
  apply(total) { return total - this.discount; }
}
const welcome = new Coupon("WELCOME", 500);

// Literal: body ditulis ulang untuk setiap data
const summer = {
  code: "SUMMER",
  discount: 1000,
  apply(total) { return total - this.discount; },
};

// Key yang dimiliki berbeda
console.log(Object.keys(welcome).join(", "));   // code, discount
console.log(Object.keys(summer).join(", "));    // code, discount, apply

Walaupun letak body-nya berbeda, welcome.apply(3000) dipanggil dengan cara yang sama persis seperti summer.apply(3000), jadi kode pemanggilnya tidak berubah. Bagaimana instance bisa memanggil method yang body-nya ada di class akan dibahas di artikel tentang prototype. Tabel di bawah membandingkan kedua cara tersebut.

AspekObject literalclass
Object yang dibuatHanya yang ditulis di { }Satu per new
Body methodDisalin ke setiap dataSekali di class
Key di Object.keyscode, discount, applycode, discount
instanceof Couponfalsetrue
Cocok untukConfig sekali pakaiBanyak data, logika sama

Jangan Beri Koma Setelah Method di Class

Saat mengubah literal menjadi class, kalau koma setelah method masih tertinggal, seperti apply(total) { ... },, muncul SyntaxError: Unexpected token ',' dan tidak ada satu baris kode pun yang berjalan. Di dalam kurung kurawal class, method cukup ditulis berurutan tanpa pemisah.

QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Setelah class Coupon {}, apa yang terjadi kalau kamu memanggil Coupon("SPRING") tanpa new?

Soal 2Di luar class ada variabel discount bernilai 300. Apa yang dikembalikan welcome.apply(3000) kalau this. terlupa?

Soal 3Apa hasil Object.keys untuk instance dari class yang punya constructor(code, discount) dan apply?