Hướng dẫn static trong javascript

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.

Hướng dẫn static trong javascript

Hướng dẫn static trong javascript

Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

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.

Ví dụ

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é.

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()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.