Menu

Static trong JavaScript: phương thức và thuộc tính static

Phương thức và thuộc tính static hoạt động thế nào trong lớp JavaScript: khi nào nên dùng, this hoạt động ra sao và mẫu factory mà chúng mang lại.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Thành viên static thuộc về lớp, không thuộc về instance

Phần lớn phương thức bạn viết trong lớp (class) làm việc trên một instance cụ thể: một user nào đó, một circle nào đó. Thành viên static thì khác. Chúng thuộc về chính lớp. Bạn gọi chúng trên tên lớp, không phải trên instance.

double nằm trên MathUtils, không nằm trên các instance của nó. Tạo instance cũng chẳng giúp gì: m.double là undefined. Điều này ngược với phương thức thường, vốn nằm trên instance (chính xác là trên prototype) và lớp không nhìn thấy chúng.

Mô hình tư duy: từ khóa class tạo ra hai thứ, một nhóm phương thức instance (dùng qua new MathUtils()) và một nhóm phương thức static (dùng trực tiếp qua MathUtils). Từ khóa static quyết định một thành viên rơi vào nhóm nào.

Cách dùng kinh điển: phương thức factory

Lý do phổ biến nhất để viết phương thức static là tạo một hàm khởi tạo thay thế. constructor thật nhận những đối số của riêng nó, nhưng bạn thường muốn có thêm cách khác để tạo đối tượng: từ JSON, từ một hàng trong cơ sở dữ liệu, từ một URL.

fromJSON không dùng một user mà tạo ra một user. Đó chính xác là lúc phương thức static phù hợp. Cách khác là một hàm parseUser đứng riêng, nhưng đặt nó trong lớp giúp gom các hành vi liên quan lại với nhau và làm rõ ý định ngay tại chỗ gọi.

Thuộc tính static

Bạn cũng có thể gắn dữ liệu vào chính lớp:

Circle.PI được mọi hình tròn dùng chung: đó là hằng số trên lớp, không được sao chép vào từng instance. Bên trong phương thức instance, bạn tham chiếu nó qua tên lớp (Circle.PI), không phải qua this.

Thuộc tính static rất tiện cho cấu hình, bộ nhớ đệm dùng chung giữa các instance, bộ đếm và hằng số cấp lớp.

this bên trong phương thức static là lớp

Bên trong phương thức thường, this là instance. Bên trong phương thức static, this là lớp:

this.count bên trong increment nghĩa là Counter.count. Thoạt đầu có thể thấy lạ, nhưng đây là điều giúp kế thừa phương thức static hoạt động: this trỏ tới lớp mà bạn gọi phương thức trên đó, không phải lớp đã định nghĩa nó.

Phương thức static và kế thừa

Phương thức static được lớp con kế thừa. Và vì this trỏ tới lớp mà phương thức được gọi trên đó, phương thức factory tự động tạo ra đúng lớp con:

Animal.create dùng new this(name). Khi bạn gọi Dog.create("Rex"), this là Dog, nên new this(name) tạo ra một Dog. Nếu viết new Animal(name) ở đó thì lúc nào cũng tạo ra Animal, làm hỏng mẫu này. Đây là lý do chính khiến this trong phương thức static trỏ tới lớp.

Static và instance: so sánh qua ví dụ

Đây là cùng một loại logic, chia theo hai phong cách:

Cả hai làm cùng một phép tính. Phiên bản instance đọc dữ liệu từ this.celsius. Phiên bản static nhận đầu vào qua đối số. Chọn phương thức instance khi thao tác là "việc mà đối tượng này làm"; chọn phương thức static khi đó là "thứ lớp này biết cách tính, khi được cho đầu vào".

Khối static để khởi tạo

Đôi khi việc khởi tạo một thuộc tính static cần nhiều hơn một biểu thức: một vòng lặp, một điều kiện, nhiều giá trị phụ thuộc nhau. Đó là việc của khối static:

Khối static { ... } chạy một lần, khi lớp được định nghĩa. Bên trong nó, this là lớp. Dùng nó cho việc khởi tạo nhiều bước; với một phép gán đơn lẻ, một trường static thông thường rõ ràng hơn.

Thành viên static private

Trường static cũng có thể là private, bằng tiền tố #. Chúng chỉ truy cập được từ bên trong lớp:

#nextId được niêm phong bên trong lớp. Code bên ngoài gọi được IdGenerator.next() nhưng không chạm vào hay đặt lại bộ đếm được. Private field sẽ có trang riêng ngay sau đây, nhưng bạn nên biết rằng kết hợp static với # là hoạt động được.

Khi nào không nên dùng static

Phương thức static là cách hay để gom các hàm trợ giúp, nhưng đó không phải lý do để biến mọi tiện ích thành lớp. Nếu bạn có một tệp đầy các hàm độc lập, hãy export các hàm đó, đừng bọc chúng trong một lớp toàn phương thức static chỉ để tạo không gian tên. Module đã làm việc đó rồi, và làm gọn gàng hơn.

Hãy dùng static khi:

  • Hàm thực sự thuộc về một lớp (factory, bộ chuyển đổi, bộ kiểm tra cho kiểu đó).
  • Bạn cần một trạng thái dùng chung giữa mọi instance của lớp đó.
  • Lớp con có thể muốn ghi đè hoặc kế thừa hành vi đó.

Còn lại, một hàm thông thường là công cụ đơn giản hơn.

Tiếp theo: private field

Bạn vừa thấy qua #nextId: đó là cú pháp private field của JavaScript. Nó dùng được cho cả thành viên instance lẫn static, và là cách hiện đại để giấu chi tiết cài đặt bên trong lớp. Đó là nội dung tiếp theo.

Câu hỏi thường gặp

Phương thức static trong JavaScript là gì?

Phương thức static thuộc về chính lớp, không thuộc về các instance của lớp. Bạn khai báo nó bằng từ khóa static và gọi nó trên lớp: MyClass.doThing(). Instance không truy cập được nó qua this.doThing(), chỉ lớp mới gọi được.

Khi nào nên dùng phương thức static thay cho phương thức instance?

Dùng phương thức static khi hàm liên quan tới lớp nhưng không cần đọc hay sửa một instance cụ thể. Các trường hợp hay gặp: phương thức factory như User.fromJSON(...), hàm tiện ích như Math.max, và hằng số thuộc về lớp như một không gian tên. Nếu phương thức cần this trỏ tới một instance thì đó là phương thức instance.

Phương thức static có truy cập được thuộc tính của instance không?

Không trực tiếp. Bên trong phương thức static, this trỏ tới lớp chứ không phải instance, nên this.name đọc một thuộc tính static chứ không phải trường của instance. Nếu cần dữ liệu của instance, hãy truyền instance vào làm đối số: static summarize(user) { return user.name; }.

Phương thức static có được kế thừa trong JavaScript không?

Có. Khi lớp con kế thừa lớp cha, các phương thức static của lớp cha cũng dùng được trên lớp con. Bên trong phương thức static, this trỏ tới lớp mà bạn gọi phương thức trên đó, nhờ vậy phương thức factory static hoạt động đúng với mọi lớp con.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu