JavaScript menggunakan model pewarisan prototipe: setiap objek mewarisi bidang (properti) dan metode dari objek prototipe.
Kelas yang digunakan di Java atau Swift sebagai template atau skema untuk membuat objek tidak ada di JavaScript. Hanya ada objek dalam pewarisan prototipe.
Pewarisan prototipe dapat meniru model klasik pewarisan kelas. Untuk melakukan ini, ES6 memperkenalkan kata kunci kelas: gula sintaksis untuk pewarisan prototipe.
Pada artikel ini, kita akan belajar cara bekerja dengan kelas: menentukan kelas, bidang dan metode privat (privat) dan publik (publik), dan membuat instance.
1. Definisi: kata kunci kelas
Kata kunci kelas digunakan untuk mendefinisikan kelas:
class User {
//
}
Sintaks ini disebut deklarasi kelas.
Kelas mungkin tidak memiliki nama. Menggunakan ekspresi kelas, Anda dapat menetapkan kelas ke variabel:
const UserClass = class {
//
}
Kelas dapat diekspor sebagai modul. Berikut contoh ekspor default:
export default class User {
//
}
Dan inilah contoh ekspor bernama:
export class User {
//
}
Kelas digunakan untuk membuat instance. Sebuah instance adalah sebuah objek yang berisi data dan logika suatu kelas.
Instance dibuat menggunakan operator new: instance = new Class ().
Berikut cara membuat instance dari kelas User:
const myUser = new User()
2. Inisialisasi: konstruktor ()
konstruktor (param1, param2, ...) adalah metode khusus di dalam kelas yang menginisialisasi sebuah instance. Di sinilah nilai awal untuk bidang instance ditetapkan dan dikonfigurasi.
Dalam contoh berikut, konstruktor menetapkan nilai awal untuk bidang nama:
class User {
constructor(name) {
this.name = name
}
}
Konstruktor mengambil satu parameter, name, yang digunakan untuk menyetel nilai awal bidang this.name.
ini di konstruktor menunjuk ke instance yang sedang dibuat.
Argumen yang digunakan untuk membuat instance kelas menjadi parameter untuk konstruktornya:
class User {
constructor(name) {
name //
this.name = name
}
}
const user = new User('')
Parameter nama di dalam konstruktor memiliki nilai 'Pechorin'.
Jika Anda tidak menentukan konstruktor Anda sendiri, akan dibuat konstruktor standar, yang merupakan fungsi kosong yang tidak memengaruhi instance.
3. Bidang
Bidang kelas adalah variabel yang berisi informasi tertentu. Bidang dapat dibagi menjadi dua kelompok:
- Bidang contoh kelas
- Bidang kelas itu sendiri (statis)
Bidang tersebut juga memiliki dua tingkat akses:
- Public (public): field tersedia baik di dalam kelas maupun dalam instance
- Pribadi (pribadi): field hanya dapat diakses di dalam kelas
3.1. Bidang publik dari instance kelas
class User {
constructor(name) {
this.name = name
}
}
Ekspresi this.name = name membuat nama bidang instance dan memberikan nilai awal untuk itu.
Bidang ini dapat diakses menggunakan pengakses properti:
const user = new User('')
user.name //
Dalam kasus ini, nama adalah bidang publik karena dapat diakses di luar kelas Pengguna.
Saat membuat bidang secara implisit di dalam konstruktor, sulit untuk mendapatkan daftar semua bidang. Untuk ini, bidang harus diambil dari konstruktor.
Cara terbaik adalah dengan mendefinisikan field kelas secara eksplisit. Tidak masalah apa yang dilakukan konstruktor, instance selalu memiliki kumpulan bidang yang sama.
Proposal untuk membuat bidang kelas memungkinkan Anda untuk menentukan bidang di dalam kelas. Selain itu, di sini Anda dapat menetapkan nilai awal ke bidang:
class SomeClass {
field1
field2 = ' '
// ...
}
Mari kita ubah kode kelas User dengan mendefinisikan field nama publik di dalamnya:
class User {
name
constructor(name) {
this.name = name
}
}
const user = new User('')
user.name //
Bidang publik ini sangat deskriptif, sekilas ke kelas memungkinkan Anda memahami struktur datanya.
Selain itu, bidang kelas dapat diinisialisasi pada saat definisi:
class User {
name = ''
constructor() {
//
}
}
const user = new User()
user.name //
Tidak ada batasan akses ke bidang terbuka dan perubahannya. Anda dapat membaca dan menetapkan nilai ke bidang tersebut di konstruktor, metode, dan di luar kelas.
3.2. Bidang pribadi dari instance kelas
Enkapsulasi memungkinkan Anda menyembunyikan detail implementasi internal kelas. Siapa pun yang menggunakan kelas yang dienkapsulasi bergantung pada antarmuka publik tanpa membahas detail implementasi kelas.
Kelas-kelas ini lebih mudah diperbarui ketika detail implementasi berubah.
Cara yang baik untuk menyembunyikan detail adalah dengan menggunakan bidang pribadi. Bidang seperti itu hanya dapat dibaca dan diubah di dalam kelas tempat mereka berasal. Bidang pribadi tidak tersedia di luar kelas.
Untuk membuat sebuah field menjadi privat, awali namanya dengan simbol #, misalnya, #myPrivateField. Saat mengacu pada bidang seperti itu, awalan yang ditentukan harus selalu digunakan.
Mari kita jadikan bidang nama pribadi:
class User {
#name
constructor(name) {
this.#name = name
}
getName() {
return this.#name
}
}
const user = new User('')
user.getName() //
user.#name // SyntaxError
#name adalah bidang pribadi. Itu hanya dapat diakses di dalam kelas Pengguna. Metode getName () melakukan ini.
Namun, mencoba mengakses #name di luar kelas User akan memunculkan kesalahan sintaks: SyntaxError: Bidang pribadi '#name' harus dideklarasikan di kelas yang melingkupinya.
3.3. Bidang statis publik
Dalam sebuah kelas, Anda dapat menentukan kolom milik kelas itu sendiri: kolom statis. Bidang tersebut digunakan untuk membuat konstanta yang menyimpan informasi yang dibutuhkan kelas.
Untuk membuat bidang statis, gunakan kata kunci statis sebelum nama bidang: myStaticField statis.
Mari tambahkan bidang tipe baru untuk menentukan tipe pengguna: administrator atau biasa. Bidang statis TYPE_ADMIN dan TYPE_REGULAR adalah konstanta untuk setiap jenis pengguna:
class User {
static TYPE_ADMIN = 'admin'
static TYPE_REGULAR = 'regular'
name
type
constructor(name, type) {
this.name = name
this.type = type
}
}
const admin = new User(' ', User.TYPE_ADMIN)
admin.type === User.TYPE_ADMIN // true
Untuk mengakses bidang statis, gunakan nama kelas dan nama properti: User.TYPE_ADMIN dan User.TYPE_REGULAR.
3.4. Bidang statis pribadi
Terkadang bidang statis juga merupakan bagian dari implementasi internal kelas. Untuk merangkum bidang semacam itu, Anda dapat menjadikannya pribadi.
Untuk melakukan ini, awali nama bidang dengan #: static #myPrivateStaticFiled.
Katakanlah kita ingin membatasi jumlah instance kelas User. Bidang statis pribadi dapat dibuat untuk menyembunyikan informasi tentang jumlah instance:
class User {
static #MAX_INSTANCES = 2
static #instances = 0
}
name
constructor(name) {
User.#instances++
if (User.#instances > User.#MAX_INSTANCES) {
throw new Error(' User')
}
this.name = name
}
new User('')
new User('')
new User('') // User
Bidang statis Pengguna. # MAX_INSTANCES menentukan jumlah instance yang diizinkan, dan Pengguna. # Contoh menentukan jumlah instance yang dibuat.
Bidang statis pribadi ini hanya tersedia di dalam kelas Pengguna. Tidak ada dari dunia luar yang dapat mempengaruhi kendala: ini adalah salah satu keuntungan enkapsulasi.
Approx. lane: jika Anda membatasi jumlah instance menjadi satu, Anda mendapatkan implementasi yang menarik dari pola desain Singleton.
4. Metode
Bidang berisi data. Kemampuan untuk mengubah data disediakan oleh fungsi khusus yang merupakan bagian dari kelas: metode.
JavaScript mendukung metode instance dan metode statis.
4.1. Metode instance
Metode dari sebuah instance kelas dapat mengubah datanya. Metode instance dapat memanggil metode instance lain serta metode statis.
Misalnya, mari kita definisikan metode getName () yang mengembalikan nama pengguna:
class User {
name = ''
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
const user = new User('')
user.getName() //
Dalam metode kelas, serta dalam konstruktor, ini menunjuk ke instance yang sedang dibuat. Gunakan ini untuk mendapatkan data instance: this.field, atau untuk memanggil metode: this.method ().
Mari tambahkan metode baru nameContains (str) yang mengambil satu argumen dan memanggil metode lain:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
nameContains(str) {
return this.getName().includes(str)
}
}
const user = new User('')
user.nameContains('') // true
user.nameContains('') // false
nameContains (str) adalah metode kelas Pengguna yang mengambil satu argumen. Ini memanggil metode instance lain getName () untuk mendapatkan nama pengguna.
Metodenya juga bisa pribadi. Untuk membuat metode menjadi pribadi, gunakan awalan #.
Mari kita jadikan metode getName () pribadi:
class User {
#name
constructor(name) {
this.#name = name
}
#getName() {
return this.#name
}
nameContains(str) {
return this.#getName().includes(str)
}
}
const user = new User('')
user.nameContains('') // true
user.nameContains('') // false
user.#getName // SyntaxError
#getName () adalah metode pribadi. Di dalam metode nameContains (str), kami menyebutnya seperti ini. # GetName ().
Menjadi pribadi, metode #getName () tidak bisa dipanggil di luar kelas Pengguna.
4.2. Getters dan Setter
Getter dan setter adalah pengakses atau properti yang dihitung. Ini adalah metode yang meniru bidang, tetapi memungkinkan Anda membaca dan menulis data.
Getters digunakan untuk menerima data, setter digunakan untuk memodifikasinya.
Untuk mencegah penugasan string kosong ke bidang nama, bungkus bidang pribadi #nameValue dalam pengambil dan penyetel:
class User {
#nameValue
constructor(name) {
this.name = name
}
get name() {
return this.#nameValue
}
set name(name) {
if (name === '') {
throw new Error(' ')
}
this.#nameValue = name
}
}
const user = new User('')
user.name // ,
user.name = '' //
user.name = '' //
4.3. Metode statis
Metode statis adalah fungsi yang dimiliki kelas itu sendiri. Mereka mendefinisikan logika kelas, bukan contohnya.
Untuk membuat metode statis, gunakan kata kunci statis di depan nama metode: myStaticMethod () statis.
Saat bekerja dengan metode statis, ada dua aturan sederhana yang perlu diingat:
- Metode statis memiliki akses ke bidang statis
- Itu tidak memiliki akses ke bidang contoh
Mari buat metode statis untuk memeriksa bahwa pengguna dengan nama yang ditentukan telah dibuat:
class User {
static #takenNames = []
static isNameTaken(name) {
return User.#takenNames.includes(name)
}
name = ''
constructor(name) {
this.name = name
User.#takenNames.push(name)
}
}
const user = new User('')
User.isNameTaken('') // true
User.isNameTaken('') // false
isNameTaken () adalah metode statis yang menggunakan bidang statis pribadi Pengguna. # takenNames untuk menentukan nama mana yang digunakan.
Metode statis juga bisa pribadi: static #myPrivateStaticMethod (). Metode tersebut hanya dapat dipanggil di dalam kelas.
5. Warisan: meluas
Kelas dalam JavaScript mendukung pewarisan menggunakan kata kunci extends.
Dalam ekspresi, kelas Anak memperluas Parent {}, kelas Anak mewarisi konstruktor, bidang, dan metode dari Induk.
Mari buat kelas anak ContentWriter yang memperluas kelas induk Pengguna:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
}
const writer = new ContentWriter('')
writer.name //
writer.getName() //
writer.posts // []
ContentWriter mewarisi dari User metode konstruktor, getName (), dan kolom nama. ContentWriter sendiri mendefinisikan bidang posting baru.
Perhatikan bahwa bidang privat dan metode kelas induk tidak diwarisi oleh kelas anak.
5.1. Konstruktor induk: super () dalam konstruktor ()
Untuk memanggil konstruktor kelas induk dalam kelas anak, gunakan fungsi super () khusus yang tersedia di konstruktor kelas anak.
Biarkan konstruktor ContentWriter memanggil konstruktor induk dan menginisialisasi kolom posts:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
constructor(name, posts) {
super(name)
this.posts = posts
}
}
const writer = new ContentWriter('', [' '])
writer.name //
writer.posts // [' ']
super (nama) di kelas anak ContentWriter memanggil pengguna konstruktor kelas induk.
Perhatikan bahwa super () dipanggil di konstruktor anak sebelum menggunakan kata kunci ini. Panggilan super () "mengikat" konstruktor induk ke instance.
class Child extends Parent {
constructor(value1, value2) {
// !
this.prop2 = value2
super(value1)
}
}
5.2. Contoh induk: super dalam metode
Untuk mengakses metode induk di dalam kelas anak, gunakan pintasan super khusus:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
constructor(name, posts) {
super(name)
this.posts = posts
}
getName() {
const name = super.getName()
if (name === '') {
return ''
}
return name
}
}
const writer = new ContentWriter('', [' '])
writer.getName() //
getName () dari kelas ContentWriter anak memanggil metode getName () dari kelas induk Pengguna.
Ini disebut metode overriding.
Perhatikan bahwa super juga dapat digunakan untuk metode statis kelas induk.
6. Pemeriksaan tipe objek: instanceof
Instance objek dari ekspresi Kelas menentukan apakah sebuah objek adalah turunan dari kelas yang ditentukan.
Mari pertimbangkan sebuah contoh:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
const user = new User('')
const obj = {}
user instanceof User // true
obj instanceof User // false
Operator instanceof bersifat polimorfik: ia memeriksa seluruh rantai kelas.
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
constructor(name, posts) {
super(name)
this.posts = posts
}
}
const writer = new ContentWriter('', [' '])
writer instanceof ContentWriter // true
writer instanceof User // true
Bagaimana jika kita perlu mendefinisikan kelas instance tertentu? Properti konstruktor dapat digunakan untuk ini:
writer.constructor === ContentWriter // true
writer.constructor === User // false
//
writer.__proto__ === ContentWriter.prototype // true
writer.__proto__ === User.prototype // false
7. Kelas dan prototipe
Harus dikatakan bahwa sintaks kelas adalah abstraksi yang bagus di atas pewarisan prototipe. Anda tidak perlu merujuk prototipe untuk menggunakan kelas.
Namun, kelas hanyalah superstruktur pada warisan prototipe. Setiap kelas adalah fungsi yang membuat instance ketika konstruktor dipanggil.
Dua contoh berikutnya identik.
Kelas:
class User {
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
const user = new User('')
user.getName() //
user instanceof User // true
Prototipe:
function User(name) {
this.name = name
}
User.prototype.getName = function () {
return this.name
}
const user = new User('')
user.getName() //
user instanceof User // true
Oleh karena itu, memahami kelas membutuhkan pengetahuan yang baik tentang pewarisan prototipe.
8. Ketersediaan kemampuan kelas
Kemampuan kelas yang disajikan dalam artikel ini dibagi antara spesifikasi ES6 dan proposal dalam pertimbangan tahap ketiga:
- Bidang contoh publik dan pribadi
- Metode dan Aksesor Instans Pribadi
- Bidang statis publik dan pribadi serta metode statis pribadi
- Spesifikasi ES6
Approx. Per: menurut Can I use, dukungan untuk bidang kelas privat saat ini 68%.
9. Kesimpulan
Kelas dalam JavaScript digunakan untuk menginisialisasi instance menggunakan konstruktor, dan menentukan bidang serta metode mereka. Kata kunci statis dapat digunakan untuk menentukan bidang dan metode kelas itu sendiri.
Warisan diimplementasikan menggunakan kata kunci extends. Kata kunci super memungkinkan Anda untuk mengakses kelas induk dari anak.
Untuk memanfaatkan enkapsulasi, mis. sembunyikan detail implementasi internal, buat kolom dan metode menjadi pribadi. Nama-nama bidang dan metode tersebut harus dimulai dengan simbol #.
Kelas ada di mana-mana dalam JavaScript modern.
Saya harap artikel ini bermanfaat bagi Anda. Terima kasih atas perhatian Anda.