Cách dùng static trong Javascript (thuộc tính và phương thức tĩnh)
Trong bài này chúng ta sẽ tìm hiểu cách sử dụng từ khóa static trong javascript, qua đó sẽ giúp bạn hiểu được cách sử dụng static để tạo ra các thuộc tính và phương thức tĩnh.
Static xuất hiện trong các ngôn ngữ lập trình hướng đối tượng là quá bình thường. Nhưng với javascript thì khác, static chỉ xuất hiện kể từ phiên bản ES6 - 2015. Vì vậy, với những trình duyệt cũ thì sẽ không hoạt động.
1. Static trong javascript là gì?
Static là một từ khóa giúp ta khai báo những phương thức tĩnh hoặc thuộc tính tĩnh trong các class của javascript. Khi được khai báo static thì phương thức / thuộc tính đó có thể được gọi đến mà không cần phải sử dụng từ khóa new để khởi tạo đối tượng.
Cú pháp như sau: Ta chỉ cần đặt từ khóa static đằng trước phương thức / thuộc tính là được.
Bài viết này được đăng tại [free tuts .net]
class className { // Static property static name = ""; // Static method static functionName(){ } }
Static method trong javascript
Static method là những phương thức có từ khóa static phía trước. Các phương thức như vậy được gọi là static method.
class User { static staticMethod() { alert(this === User); } } // Không cần phải khởi tạo User.staticMethod(); // true
Điều này giống như bạn tạo một phương thức từ bên ngoài lớp như sau:
// Khai báo lớp class User { } // Thêm một static method User.staticMethod = function() { alert(this === User); }; User.staticMethod(); // true
Qua 2 ví dụ này thì ta thấy từ khóa this
bên trong static method chính là class của nó.
Tuy nhiên, ta không thể sử dụng từ khóa this để gọi đến một phương thức không phải là static.
class User { sayHi() { console.log("Xin chào"); } static staticMethod() { // Sai, vì sayHi không phải là static this.sayHi(); } } // Không cần phải khởi tạo User.staticMethod(); // true
Static properties trong javascript
Static properties hay còn gọi là thuộc tính tĩnh, là những thuộc tính có đặt từ khóa static phía trước.
Những thuộc tính như vậy ta có thể truy cập đến mà không cần phải khởi tạo đối tượng.
class Article { static publisher = "Cường Nguyễn"; } alert( Article.publisher ); // Cường Nguyễn
2. Sử dụng this để truy cập thuộc tính static
Nếu bạn sử dụng this
để truy cập đến một thuộc tính static thì nó sẽ trả về undefined. Bởi vì một thuộc tính static sẽ không nằm trong danh sách thuộc tính của class, mà nó được lưu trữ trong constructor của class.
class Article { static publisher = "Cường Nguyễn" show() { // Xuất ra undefined alert(this.publisher); } } let a = new Article(); a.show(); // kết quả là undefined
Thử sử dụng lệnh console.log để xem trong biến a có gì nhé.
console.log(a);
Như bạn thấy ở trong hình, thuộc tính static publisher đã được lưu trữ trong constructor. Vì vậy, nếu bạn muốn sử dụng this
để truy cập đến nó thì phải sử dụng cú pháp this.constructor.publisher
:
class Article { static publisher = "Cường Nguyễn" show() { alert(this.constructor.publisher); } } let a = new Article(); a.show(); // Cường Nguyễn
3. Thuộc tính static có giá trị duy nhất
Thuộc tính / phương thức static trong javascript là duy nhất nhé các bạn. Vì nó được lưu trữ trong constructor của class, mà dữ liệu trong constructor là duy nhất, nghĩa là những thay đổi bên trong constructor là ảnh hưởng đến đối tượng chứ không phải instance.
Đọc tới đây chắc nhiều bạn không hiểu instance là gì, thì mình xin giải thích như sau:
Ví dụ mình có lớp A, và mình tạo 2 biến như sau:
class A{ // code } let instance1 = new A(); let instance2 = new A();
Hai biến instance1 và instance2 ta gọi là các thể hiện (instance) của đối tượng A.
Quay lại vấn đề chính. Giả sử mình có class Article như sau:
class Article { static publisher = "Cường Nguyễn" change(new_value){ this.constructor.publisher = new_value; } show() { console.log(this.constructor.publisher); } }
Trong đó, hàm change()
mình tạo ra với mục đích thay đổi dữ liệu cho thuộc tính static publisher
.
Bây giờ mình chạy đoạn code dưới đây:
let a = new Article(); a.show(); // Cường Nguyễn let b = new Article(); b.change('Nguyễn Văn Cường'); b.show(); // Nguyễn Văn Cường a.show(); // Nguyễn Văn Cường console.log(Article.publisher); // Nguyễn Văn Cường
Như bạn thấy,
- Ban đầu a.show() sẽ in ra giá trị là Cường Nguyễn.
- Sau khi chạy hàm
b.change()
thì giá trị của publisher đã thay đổi - Tiếp tục chạy
b.show()
vàa.show()
thì đều cho ra kết quả giống nhau, và đó là giá trị mới thay đổi.
Điều này chứng tỏ thuộc tính publisher có giá trị duy nhất, bởi nó là dữ liệu của class chứ không phải trên instance.
4. Static trong kế thừa thuộc tính và phương thức
Một điều khá thú vị nữa, đó là nếu một thuộc tính là static thì trong kế thừa sẽ như thế nào? Để hiểu vấn đề này thì hơi hại não một chút, nên các bạn cần tập trung để xem những giải thích của mình nhé.
Đầu tiên bạn phải hiểu dữ liệu __proto__.constructor
của một instance.
Khi bạn tạo một instance thì instance đó sẽ có một thuộc tính tên là __proto__
. Trong __proto__
sẽ có một thuộc tính tên là constructor
. Đây chính là thông tin của class dùng để tạo ra biến instance đó.
class Post { show(){ // code } } let p = new Post(); console.log(p);
Kêt quả trên cửa sổ console như sau:
Mình thử dùng phép toán so sánh thì kết quả là true:
console.log(p.__proto__.constructor == Post); // True
Bây giờ mình sẽ cho class Post kế thừa một class khác, sau đó dùng console.log để xem:
class Article { static publisher = "Cường Nguyễn" } class Post extends Article{ show(){ // code } } let p = new Post(); console.log(p); // True
Kết quả:
Như vậy class Article sẽ nằm trong hai vị trí:
- Thứ nhất là
__proto__.constructor.__proto__
của Post - Thứ hai là trong
__proto__.__proto__.constructor
của Post
Ta thử dùng phép so sánh xem có chuẩn không nhé.
console.log(p.__proto__.constructor.__proto__ == Article); // True console.log(p.__proto__.__proto__.constructor == Article); // True
Cấu trúc dữ liệu của các đối tượng trong javascript quả là phức tạp phải không các bạn? Mục đích mình giải thích ở trên là giúp các bạn hiểu được một class kế thừa thì nó có cấu trúc như thế nào.
Bây giờ ta sẽ đi vào vấn đề chính nhé. Vẫn tiếp tục lấy hai class dưới đây làm ví dụ:
class Article { static publisher = "Cường Nguyễn" } class Post extends Article{ show(){ // code } }
Bây giờ thử in thuộc tính publisher
xem giá trị thế nào:
console.log(Post.publisher); // Cường Nguyễn console.log(Article.publisher); // Cường Nguyễn
Cả hai đều cho một kết quả. Bây giờ ta thử thay đổi giá trị của publisher
.
console.log(Post.publisher); // Cường Nguyễn console.log(Article.publisher); // Cường Nguyễn Post.publisher = "Nguyễn Văn Cường"; console.log(Post.publisher); // Nguyễn Văn Cường console.log(Article.publisher); // Cường Nguyễn
Trường hợp này đã xuất hiện sự sai lệch. Nếu lớp con thay đổi giá trị static của lớp cha thì nó chỉ thay đổi cho lớp con mà thôi.
Bây giờ ta thử tạo mới một instance, sau đó log ra xem có gì nhé.
Post.publisher = "Nguyễn Văn Cường"; let t = new Post(); console.log(t);
Các bạn thấy có sự sai lệch rồi phải không? Có vẻ như do mình sử dụng phép gán nên javascript sẽ tạo ra một thuộc tính static mới trên lớp Post. Vì vậy khi truy cập thì javascript vẫn ưu tiên lấy ở lớp Post.
Bây giờ ta thử hoán đổi hai class xem thế nào.
console.log(Post.publisher); // Cường Nguyễn console.log(Article.publisher); // Cường Nguyễn Article.publisher = "Nguyễn Văn Cường"; console.log(Post.publisher); // Nguyễn Văn Cường console.log(Article.publisher); // Nguyễn Văn Cường
Kết quả là dữ liệu ở cả hai class đều thay đổi.
Ta thử chạy lệnh dưới đây để xem điêu gì đã xảy ra nhé.
Article.publisher = "Nguyễn Văn Cường"; let t = new Post(); console.log(t);
Mọi thứ suôn sẻ, trong lớp Post không có thuộc tính publisher, nên khi gọi đến thuộc tính này thì cả hai class đều lấy chung một thuộc tính ở lớp Article.
Lời kết: Như vậy là mình đã hướng dẫn xong cách sử dụng static trong Javascript. Đây là một kiến thức khá quan trọng, nó giúp bạn hiểu được khái niệm static là gì, khi nào nên sử dụng static, và cách sử dụng static trong kế thừa. Hẹn gặp lại các bạn ở bài tiếp theo.