Dalam pemrograman berorientasi objek, kelas adalah templat kode program yang dapat diperluas untuk membuat objek, memberikan nilai awal untuk keadaan (variabel anggota) dan implementasi perilaku (fungsi atau metode anggota).
Dalam praktiknya, kita sering kali perlu membuat banyak objek dengan jenis yang sama, seperti pengguna, barang, atau apa pun.
Seperti yang telah kita ketahui dari bab Konstruktor, operator "baru", new function
dapat membantu dalam hal itu.
Namun dalam JavaScript modern, terdapat konstruksi “kelas” yang lebih canggih, yang memperkenalkan fitur-fitur baru yang hebat yang berguna untuk pemrograman berorientasi objek.
Sintaks dasarnya adalah:
kelas Kelasku { // metode kelas konstruktor() { ... } metode1() { ... } metode2() { ... } metode3() { ... } ... }
Kemudian gunakan new MyClass()
untuk membuat objek baru dengan semua metode yang tercantum.
Metode constructor()
dipanggil secara otomatis oleh new
, sehingga kita dapat menginisialisasi objek di sana.
Misalnya:
pengguna kelas { konstruktor(nama) { ini.nama = nama; } ucapkan Hai() { alert(ini.nama); } } // Penggunaan: biarkan pengguna = Pengguna baru("John"); pengguna.sayHi();
Saat new User("John")
dipanggil:
Sebuah objek baru dibuat.
constructor
berjalan dengan argumen yang diberikan dan menugaskannya ke this.name
.
…Kemudian kita dapat memanggil metode objek, seperti user.sayHi()
.
Tidak ada koma antar metode kelas
Kesalahan umum bagi pengembang pemula adalah memberi tanda koma di antara metode kelas, yang akan mengakibatkan kesalahan sintaksis.
Notasi di sini berbeda dengan literal objek. Di dalam kelas, koma tidak diperlukan.
Jadi, apa sebenarnya class
itu? Itu bukanlah entitas tingkat bahasa yang benar-benar baru, seperti yang mungkin dipikirkan.
Mari kita mengungkap keajaiban apa pun dan melihat apa sebenarnya kelas itu. Itu akan membantu dalam memahami banyak aspek kompleks.
Dalam JavaScript, kelas adalah sejenis fungsi.
Di sini, lihat:
pengguna kelas { konstruktor(nama) { ini.nama = nama; } sayHi() { alert(ini.nama); } } // bukti: Pengguna adalah sebuah fungsi peringatan(tipe Pengguna); // fungsi
Apa yang sebenarnya dilakukan oleh konstruksi class User {...}
adalah:
Membuat fungsi bernama User
, yang menjadi hasil deklarasi kelas. Kode fungsi diambil dari metode constructor
(dianggap kosong jika kita tidak menulis metode tersebut).
Menyimpan metode kelas, seperti sayHi
, di User.prototype
.
Setelah objek new User
dibuat, ketika kita memanggil metodenya, metode tersebut diambil dari prototipe, seperti yang dijelaskan dalam bab F.prototype. Jadi objek tersebut memiliki akses ke metode kelas.
Kita dapat mengilustrasikan hasil deklarasi class User
sebagai:
Berikut kode untuk melakukan introspeksi:
pengguna kelas { konstruktor(nama) { ini.nama = nama; } sayHi() { alert(ini.nama); } } // kelas adalah sebuah fungsi peringatan(tipe Pengguna); // fungsi // ...atau, lebih tepatnya, metode konstruktor alert(Pengguna === Pengguna.prototipe.konstruktor); // BENAR // Metodenya ada di User.prototype, misal: alert(Pengguna.prototipe.sayHi); // kode metode sayHi // ada dua metode dalam prototipe alert(Object.getOwnPropertyNames(User.prototype)); // konstruktor, sapa
Kadang-kadang orang mengatakan bahwa class
adalah “gula sintaksis” (sintaks yang dirancang untuk membuat segalanya lebih mudah dibaca, tetapi tidak memperkenalkan sesuatu yang baru), karena kita sebenarnya bisa mendeklarasikan hal yang sama tanpa menggunakan kata kunci class
sama sekali:
// menulis ulang kelas Pengguna dalam fungsi murni // 1. Membuat fungsi konstruktor fungsi Pengguna(nama) { ini.nama = nama; } // prototipe fungsi memiliki properti "konstruktor" secara default, // jadi kita tidak perlu membuatnya // 2. Tambahkan metode ke prototipe Pengguna.prototipe.sayHi = function() { alert(ini.nama); }; // Penggunaan: biarkan pengguna = Pengguna baru("John"); pengguna.sayHi();
Hasil dari definisi ini hampir sama. Jadi, memang ada alasan mengapa class
dapat dianggap sebagai gula sintaksis untuk mendefinisikan konstruktor bersama dengan metode prototipenya.
Namun, ada perbedaan penting.
Pertama, fungsi yang dibuat oleh class
diberi label oleh properti internal khusus [[IsClassConstructor]]: true
. Jadi tidak sepenuhnya sama dengan membuatnya secara manual.
Bahasa memeriksa properti itu di berbagai tempat. Misalnya, tidak seperti fungsi biasa, fungsi tersebut harus dipanggil dengan new
:
pengguna kelas { konstruktor() {} } peringatan(tipe Pengguna); // fungsi Pengguna(); // Kesalahan: Pengguna konstruktor kelas tidak dapat dipanggil tanpa 'baru'
Selain itu, representasi string dari konstruktor kelas di sebagian besar mesin JavaScript dimulai dengan “kelas…”
pengguna kelas { konstruktor() {} } peringatan(Pengguna); // Pengguna kelas { ... }
Ada perbedaan lain, kita akan segera melihatnya.
Metode kelas tidak dapat dihitung. Definisi kelas menyetel tanda enumerable
ke false
untuk semua metode dalam "prototype"
.
Itu bagus, karena jika kita for..in
pada suatu objek, kita biasanya tidak menginginkan metode kelasnya.
Kelas selalu use strict
. Semua kode di dalam konstruksi kelas secara otomatis berada dalam mode ketat.
Selain itu, sintaksis class
menghadirkan banyak fitur lain yang akan kita jelajahi nanti.
Sama seperti fungsi, kelas dapat didefinisikan di dalam ekspresi lain, diteruskan, dikembalikan, ditetapkan, dll.
Berikut ini contoh ekspresi kelas:
biarkan Pengguna = kelas { ucapkan Hai() { peringatan("Halo"); } };
Mirip dengan Ekspresi Fungsi Bernama, ekspresi kelas mungkin memiliki nama.
Jika ekspresi kelas memiliki nama, maka ekspresi tersebut hanya terlihat di dalam kelas:
// "Ekspresi Kelas Bernama" // (tidak ada istilah seperti itu dalam spesifikasi, tapi itu mirip dengan Ekspresi Fungsi Bernama) biarkan Pengguna = kelas Kelas Saya { ucapkan Hai() { peringatan(Kelas Saya); // Nama MyClass hanya terlihat di dalam kelas } }; Pengguna baru().sayHi(); // berfungsi, menampilkan definisi MyClass peringatan(Kelas Saya); // error, nama MyClass tidak terlihat di luar kelas
Kita bahkan dapat membuat kelas secara dinamis “sesuai permintaan”, seperti ini:
fungsi makeClass(frasa) { // mendeklarasikan kelas dan mengembalikannya kelas kembali { ucapkan Hai() { peringatan(frasa); } }; } // Buat kelas baru biarkan Pengguna = makeClass("Halo"); Pengguna baru().sayHi(); // Halo
Sama seperti objek literal, kelas dapat mencakup getter/setter, properti terkomputasi, dll.
Berikut ini contoh untuk user.name
yang diimplementasikan menggunakan get/set
:
pengguna kelas { konstruktor(nama) { // memanggil penyetel ini.nama = nama; } dapatkan nama() { kembalikan ini._nama; } tetapkan nama(nilai) { if (nilai.panjang < 4) { alert("Nama terlalu pendek."); kembali; } this._name = nilai; } } biarkan pengguna = Pengguna baru("John"); alert(nama pengguna); // Yohanes pengguna = Pengguna baru(""); // Nama terlalu pendek.
Secara teknis, deklarasi kelas tersebut bekerja dengan membuat getter dan setter di User.prototype
.
Berikut ini contoh nama metode yang dihitung menggunakan tanda kurung [...]
:
pengguna kelas { ['ucapkan' + 'Hai']() { peringatan("Halo"); } } Pengguna baru().sayHi();
Ciri-ciri tersebut mudah diingat karena mirip dengan objek literal.
Browser lama mungkin memerlukan polyfill
Bidang kelas adalah tambahan terbaru pada bahasa ini.
Sebelumnya, kelas kami hanya memiliki metode.
"Bidang kelas" adalah sintaks yang memungkinkan untuk menambahkan properti apa pun.
Misalnya, mari tambahkan properti name
ke class User
:
pengguna kelas { nama = "Yohanes"; ucapkan Hai() { alert(`Halo, ${ini.nama}!`); } } Pengguna baru().sayHi(); // Halo, John!
Jadi, kita tulis saja “
Perbedaan penting dari bidang kelas adalah bahwa bidang tersebut disetel pada objek individual, bukan User.prototype
:
pengguna kelas { nama = "Yohanes"; } biarkan pengguna = Pengguna baru(); alert(nama pengguna); // Yohanes alert(Pengguna.prototipe.nama); // belum diartikan
Kita juga dapat menetapkan nilai menggunakan ekspresi dan pemanggilan fungsi yang lebih kompleks:
pengguna kelas { nama = prompt("Tolong beri nama?", "John"); } biarkan pengguna = Pengguna baru(); alert(nama pengguna); // Yohanes
Seperti yang ditunjukkan dalam bab Fungsi pengikatan fungsi dalam JavaScript memiliki this
yang dinamis. Itu tergantung pada konteks panggilannya.
Jadi jika suatu metode objek diteruskan dan dipanggil dalam konteks lain, this
tidak akan menjadi referensi ke objeknya lagi.
Misalnya, kode ini akan menampilkan undefined
:
Tombol kelas { konstruktor(nilai) { this.nilai = nilai; } klik() { alert(ini.nilai); } } biarkan tombol = Tombol baru("halo"); setTimeout(tombol.klik, 1000); // belum diartikan
Masalahnya disebut “kehilangan this
”.
Ada dua pendekatan untuk memperbaikinya, seperti yang dibahas dalam bab Pengikatan fungsi:
Berikan fungsi pembungkus, seperti setTimeout(() => button.click(), 1000)
.
Ikat metode ke objek, misalnya di konstruktor.
Bidang kelas menyediakan sintaks lain yang cukup elegan:
Tombol kelas { konstruktor(nilai) { this.nilai = nilai; } klik = () => { alert(ini.nilai); } } biarkan tombol = Tombol baru("halo"); setTimeout(tombol.klik, 1000); // Halo
Bidang kelas click = () => {...}
dibuat berdasarkan per objek, ada fungsi terpisah untuk setiap objek Button
, dengan this
di dalamnya mereferensikan objek tersebut. Kita dapat meneruskan button.click
ke mana saja, dan this
akan selalu benar.
Itu sangat berguna di lingkungan browser, untuk pendengar acara.
Sintaks kelas dasar terlihat seperti ini:
kelas Kelasku { penyangga = nilai; // properti konstruktor(...) { // konstruktor // ... } metode(...) {} // metode dapatkan sesuatu(...) {} // metode pengambil mengatur sesuatu(...) {} // metode penyetel [Symbol.iterator]() {} // metode dengan nama yang dihitung (simbol di sini) // ... }
MyClass
secara teknis adalah sebuah fungsi (yang kami sediakan sebagai constructor
), sedangkan metode, pengambil, dan penyetel ditulis ke MyClass.prototype
.
Pada bab selanjutnya kita akan mempelajari lebih lanjut tentang kelas, termasuk pewarisan dan fitur lainnya.
pentingnya: 5
Kelas Clock
(lihat kotak pasir) ditulis dalam gaya fungsional. Tulis ulang dalam sintaks "kelas".
PS Jam terus berdetak di konsol, buka untuk melihat.
Buka kotak pasir untuk tugas tersebut.
kelas Jam { konstruktor({ templat }) { this.template = templat; } memberikan() { biarkan tanggal = Tanggal baru(); biarkan jam = tanggal.getHours(); if (jam < 10) jam = '0' + jam; biarkan menit = tanggal.getMinutes(); if (menit < 10) menit = '0' + menit; biarkan detik = tanggal.getSeconds(); if (detik < 10) detik = '0' + detik; biarkan keluaran = ini.template .replace('h', jam) .replace('m', menit) .replace('s', detik); konsol.log(keluaran); } berhenti() { clearInterval(ini.timer); } awal() { ini.render(); this.timer = setInterval(() => this.render(), 1000); } } biarkan jam = Jam baru({templat: 'h:m:s'}); jam.mulai();
Buka solusi di kotak pasir.