Nội dung chính
1. Phương thức tĩnh
Chúng ta cũng có thể gọi hoặc gán một phương thức từ chính class mà không cần phải tạo đối tượng của class đó, không phải cho "prototype"
của nó. Các phương pháp như vậy được gọi là phương thức tĩnh.
Trong một lớp, chúng có thêm từ khóa static
, như thế này:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
class User {
static staticMethod() {
alert(this === User);
}
}
User.staticMethod(); // true
Điều đó thực sự giống như việc gán nó trực tiếp như một thuộc tính:
class User { }
User.staticMethod = function() {
alert(this === User);
};
User.staticMethod(); // true
Giá trị của this
trong lệnh gọiUser.staticMethod()
là contructor riêng của lớp User
(quy tắc “đối tượng trước khi chấm” ).
Thông thường, các phương thức tĩnh được sử dụng để thực hiện các hàm thuộc về lớp, nhưng không thuộc về bất kỳ đối tượng cụ thể nào của nó.
Chẳng hạn, chúng ta có các đối tượngArticle
và cần một hàm để so sánh chúng. Một giải pháp tự nhiên là thêm phương thức Article.compare
, như thế này:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static compare(articleA, articleB) {
return articleA.date - articleB.date;
}
}
// usage
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
Ở đây Article.compare
như một phương tiện để so sánh chúng. Đây không phải là một phương pháp của riêng một bài viết nào cả, mà là của cả lớp.
Một ví dụ khác là phương thức được gọi là phương thức “factory”. Hãy tưởng tượng, chúng ta cần một vài cách để tạo một bài viết:
- Tạo bởi thông số nhất định (
title
,date
vv). - Tạo một bài viết trống với ngày hôm nay.
- Nếu không thì bằng cách nào đó.
Cách đầu tiên có thể được thực hiện bởi các nhà xây dựng(contructor). Và đối với cái thứ hai, chúng ta có thể tạo một phương thức tĩnh của lớp.
Giống như Article.createTodays()
ở đây:
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static createTodays() {
// remember, this = Article
return new this("Today's digest", new Date());
}
}
let article = Article.createTodays();
alert( article.title ); // Today's digest
Bây giờ mỗi khi chúng ta cần tạo ra một thông báo ngày hôm nay, chúng ta có thể gọi Article.createTodays()
. Một lần nữa, đó không phải là một phương thức của một bài viết, mà là một phương thức của cả lớp.
Các phương thức tĩnh cũng được sử dụng trong các lớp liên quan đến cơ sở dữ liệu để tìm kiếm / lưu / xóa các mục khỏi cơ sở dữ liệu, như sau:
// assuming Article is a special class for managing articles
// static method to remove the article:
Article.remove({id: 12345});
2. Thuộc tính tĩnh
Các thuộc tính tĩnh cũng có thể giống như hàm tĩnh ở trên, chúng trông giống như các thuộc tính của lớp thông thường, nhưng được có thêm static
:
class Article {
static publisher = "Ilya Kantor";
}
alert( Article.publisher ); // Ilya Kantor
Điều đó giống như làm một cái gì trực tiếp cho Article
:
Article.publisher = "Ilya Kantor";
3. Kế thừa các thuộc tính và phương thức tĩnh
Các thuộc tính và phương thức tĩnh được kế thừa.
Chẳng hạn, Animal.compare
và Animal.planet
trong đoạn code dưới đây được kế thừa và có thể truy cập như Rabbit.compare
và Rabbit.planet
:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
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;
}
}
// Inherit from 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
Bây giờ khi chúng ta gọi Rabbit.compare
, hàm Animal.compare
sẽ được gọi.
Làm thế nào nó hoạt động? Một lần nữa, sử dụng nguyên mẫu. Như bạn có thể đoán, extends
Rabbit
thì [[Prototype]]
sẽ tham chiếu tới Animal
.
Vì vậy, Rabbit extends Animal
tạo hai [[Prototype]]
tham chiếu:
- Phương thức của
Rabbit
kế thừa nguyên mẫu từ phương thức củaAnimal
. Rabbit.prototype
nguyên mẫu kế thừa từAnimal.prototype
.
Kết quả là, kế thừa hoạt động cả cho các phương thức thường và tĩnh.
Ở đây, hãy kiểm tra bằng code:
class Animal {}
class Rabbit extends Animal {}
// for statics
alert(Rabbit.__proto__ === Animal); // true
// for regular methods
alert(Rabbit.prototype.__proto__ === Animal.prototype); // true
4. Tóm lược
Các phương thức tĩnh được sử dụng cho các chức năng thuộc về lớp đó. Nó không liên quan đến một thể hiện cụ thể nào của lớp.
Ví dụ, một phương pháp để so sánh Article.compare(article1, article2)
hoặc một phương pháp tĩnh Article.createTodays()
.
Chúng được thêm từ khoá static
trong khai báo lớp.
Các thuộc tính tĩnh được sử dụng khi chúng ta muốn lưu trữ dữ liệu cấp độ lớp, cũng không bị ràng buộc với một thể hiện.
Cú pháp là:
class MyClass {
static property = ...;
static method() {
...
}
}
Về mặt kỹ thuật, khai báo tĩnh giống như việc gán cho chính lớp đó:
MyClass.property = ...
MyClass.method = ...
Các thuộc tính và phương thức tĩnh có thể được kế thừa.
Đối với class B extends A
thì nguyên mẫu của lớp B
sẽ trỏ đến A
: B.[[Prototype]] = A
. Vì vậy, nếu một trường không được tìm thấy trong B
thì sẽ tìm kiếm tiếp tục trên A
.
Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!