15 Desember 2021

Properti dan metode statis

Kita juga dapat menetapkan metode ke fungsi kelas itu sendiri, bukan ke " prototipe "-nya. Metode seperti itu disebut static.

Di dalam kelas, mereka ditambahkan oleh kata kunci static, seperti ini:

class User {
  static staticMethod() {
    alert(this === User);
  }
}

User.staticMethod(); // true

Itu sebenarnya sama dengan menetapkannya sebagai properti secara langsung:

class User {}

User.staticMethod = function () {
  alert(this === User);
};

User.staticMethod(); // true

Nilai this dalam panggilan User.staticMethod() adalah konstruktor kelas User itu sendiri (aturan “object before dot”).

Biasanya, metode statis digunakan untuk mengimplementasikan fungsi yang dimiliki kelas, tetapi tidak untuk objek tertentu darinya.

Misalnya, kita punya objek Article dan membutuhkan sebuah fungsi untuk membandingkan mereka. Solusi natural adalah menambahkan metode Article.compare, seperti ini:

class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static compare(articleA, articleB) {
    return articleA.date - articleB.date;
  }
}

// penggunaan
let articles = [
  new Article("HTML", new Date(2019, 1, 1)),
  new Article("CSS", new Date(2019, 0, 1)),
  new Article("JavaScript", new Date(2019, 11, 1))
];

articles.sort(Article.compare);

alert( articles[0].title ); // CSS

Di sini Article.compare berdiri “di atas” articles, sebagai alat untuk membandingkannya. Ini bukan metode article, melainkan seluruh kelas.

Contoh lain adalah apa yang disebut metode “factory”. Bayangkan, kita butuh beberapa cara untuk membuat article:

  1. Buat dengan parameter yang diberikan (title, date dsb).
  2. Buat article kosong dengan tanggal hari ini.
  3. …atau yang lainnya.

Cara pertama dapat diterapkan oleh konstruktor. Dan untuk yang kedua kita bisa membuat metode statis kelas.

Seperti Article.createTodays() di sini:

class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static createTodays() {
    // ingat, this = Article
    return new this("Today's digest", new Date());
  }
}

let article = Article.createTodays();

alert( article.title ); // Today's digest

Sekarang setiap kali kita perlu membuat today’s digest, kita dapat memanggil Article.createTodays(). Sekali lagi, itu bukan metode article, tapi metode seluruh kelas.

Metode statis juga digunakan dalam kelas terkait basis data untuk mencari/menyimpan/menghapus entri dari basis data, seperti ini:

// dengan asumsi Article adalah kelas khusus untuk mengelola articles
// metode statis untuk menghapus article:
Article.remove({ id: 12345 });

Properti Statis

Tambahan baru-baru ini
Ini adalah tambahan terbaru untuk bahasa ini. Contoh berfungsi di Chrome baru-baru ini.

Properti statis juga dimungkinkan, mereka terlihat seperti properti kelas biasa, tetapi diawali dengan static:

class Article {
  static publisher = 'Ilya Kantor';
}

alert(Article.publisher); // Ilya Kantor

Itu sama dengan penugasan langsung ke Article:

Article.publisher = 'Ilya Kantor';

Pewarisan properti dan metode statis

Properti dan metode statis diwarisi.

Misalnya, Animal.compare dan Animal.planet dalam kode di bawah ini diwariskan dan dapat diakses sebagai Rabbit.compare dan Rabbit.planet:

class Animal {
  static planet = "Earth";

  constructor(name, speed) {
    this.speed = speed;
    this.name = name;
  }

  run(speed = 0) {
    this.speed += speed;
    alert(`${this.name} runs with speed ${this.speed}.`);
  }

  static compare(animalA, animalB) {
    return animalA.speed - animalB.speed;
  }

}

// Mewarisi dari Animal
class Rabbit extends Animal {
  hide() {
    alert(`${this.name} hides!`);
  }
}

let rabbits = [
  new Rabbit("White Rabbit", 10),
  new Rabbit("Black Rabbit", 5)
];

rabbits.sort(Rabbit.compare);

rabbits[0].run(); // Black Rabbit runs with speed 5.

alert(Rabbit.planet); // Earth

Sekarang kita dapat memanggil Rabbit.compare, yang diwariskan Animal.compare akan dipanggil.

Bagaimana cara kerjanya? Sekali lagi, menggunakan prototipe. Seperti yang mungkin sudah kamu duga, extends memberi Rabbit sebagai [[Prototype]] mengacu kepada Animal.

Jadi, Rabbit extends Animal membuat dua acuan [[Prototype]]:

  1. Rabbit fungsi prototypally mewarisi dari fungsi Animal.
  2. Rabbit.prototype prototypally mewarisi dari Animal.prototype.

Hasilnya, pewarisan berfungsi baik untuk metode reguler dan statis.

Di sini, mari kita periksa dengan kode:

class Animal {}
class Rabbit extends Animal {}

// untuk statis
alert(Rabbit.__proto__ === Animal); // true

// untuk metode reguler
alert(Rabbit.prototype.__proto__ === Animal.prototype); // true

Ringkasan

Metode statis digunakan untuk fungsionalitas yang termasuk dalam kelas “secara keseluruhan”. Ini tidak terkait dengan instance kelas konkret.

Sebagai contoh, metode perbandingan Article.compare(article1, article2) atau metode factory Article.createTodays().

Mereka diberi label dengan kata static dalam deklarasi kelas.

Properti statis digunakan ketika kita ingin menyimpan data tingkat kelas, juga tidak terikat pada sebuah instance.

Sintaksnya adalah:

class MyClass {
  static property = ...;

  static method() {
    ...
  }
}

Secara teknis, deklarasi statis sama dengan menetapkan ke kelas itu sendiri:

MyClass.property = ...
MyClass.method = ...

Properti dan metode statis diwarisi.

Untuk class B extends A prototipe dari kelas B itu sendiri menunjuk ke A: B.[[Prototype]] = A. Jadi jika bidang tidak ditemukan di B, pencarian dilanjutkan di A.

Tugas

pentingnya: 3

Seperti yang kita ketahui, semua objek biasanya diwarisi dari Object.prototype dan mendapatkan akses ke metode objek “generic” seperti hasOwnProperty dll.

Misalnya:

class Rabbit {
  constructor(name) {
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

// metode hasOwnProperty dari Object.prototype
alert( rabbit.hasOwnProperty('name') ); // true

Tapi jika kita mengejanya secara eksplisit seperti "class Rabbit extends Object", maka hasilnya akan berbeda dari "class Rabbit"?

Apa perbedaannya?

Berikut contoh kodenya (tidak berhasil – mengapa? memperbaikinya?):

class Rabbit extends Object {
  constructor(name) {
    this.name = name;
  }
}

let rabbit = new Rabbit('Rab');

alert(rabbit.hasOwnProperty('name')); // Error

Pertama, mari kita lihat mengapa kode terakhir tidak berfungsi.

Alasannya menjadi jelas jika kita mencoba menjalankannya. Konstruktor kelas yang mewarisi harus memanggil super(). Jika tidak, "this" tidak akan “defined”.

Jadi, inilah perbaikannya:

class Rabbit extends Object {
  constructor(name) {
    super(); // perlu memanggil konstruktor induk saat mewarisi
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

alert( rabbit.hasOwnProperty('name') ); // true

Tapi itu belum semuanya.

Bahkan setelah perbaikan, masih ada perbedaan penting dalam "class Rabbit extends Object" versus class Rabbit.

Seperti yang kita tahu, sintaks “extends” menyiapkan dua prototipe:

  1. Antara "prototype" dari fungsi konstruktor (untuk metode).
  2. Antara konstruktor berfungsi sendiri (untuk metode statis).

Dalam kasus kita, untuk class Rabbit extends Object itu berarti:

class Rabbit extends Object {}

alert(Rabbit.prototype.__proto__ === Object.prototype); // (1) true
alert(Rabbit.__proto__ === Object); // (2) true

Jadi Rabbit sekarang menyediakan akses ke metode statis Object melalui Rabbit, seperti ini:

class Rabbit extends Object {}

// biasanya kita sebut Object.getOwnPropertyNames
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b

Tetapi jika kita tidak punya extends Object, lalu Rabbit.__proto__ tidak diatur ke Object.

Berikut demo nya:

class Rabbit {}

alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) false (!)
alert( Rabbit.__proto__ === Function.prototype ); // sebagai fungsi apa pun secara default

// error, tidak ada fungsi seperti itu di Rabbit
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error

Jadi Rabbit tidak menyediakan akses ke metode statis Object dalam hal itu.

Ngomong-ngomong, Function.prototype mempunyai fungsi metode “generic”, seperti call, bind dll. Mereka terakhir tersedia dalam kedua kasus, karena untuk konstruktor Object bawaan, Object.__proto__ === Function.prototype.

Berikut gambarnya:

Jadi, sederhananya, ada dua perbedaan:

class Rabbit class Rabbit extends Object
needs to call super() in constructor
Rabbit.__proto__ === Function.prototype Rabbit.__proto__ === Object
Peta tutorial