Prototype của object

1302

Bài viết được sự cho phép của tác giả Lưu Bình An

Khi chúng ta console.log một object của JS, sẽ thấy một property ẩn đặc biệt [[Prototype]], nó có thể là null hoặc là trỏ đến một object khác

object a => [[Prototype]] => prototype object b

Điều này có nghĩa là object a kế thừa từ object b, b có gì thì a sẽ có đó

  Javascript prototype chuyên sâu
  Nếu vỗ ngực xưng tên là một javascript developer sành sỏi, mà không giải thích được prototype inheritance thì thật là kỳ

Xem thêm nhiều việc làm JavaScript hấp dẫn trên TopDev

Chúng ta không thể truy xuất trực tiếp thông qua [[Prototype]], mà thông qua các phương pháp khác

let animal = {
    eats: true,
    walk() {
        alert("Animal walk");
    }
}

let rabbit = {
    jumps: true,
    __proto__: animal}

// hoặc khai báo bằng
rabbit.__proto__ = animal;
console.log(rabbit.eats);
// => true;

rabbit.walk();

__proto__ != [[Prototype]]

Về bản chất, __proto__ không phải là property [[Prototype]], chính xác thì nó là getter/setter của [[Prototype]]

Thời điểm hiện tại, không khuyến khích dùng __proto__, thay vào đó dùng Object.getPrototypeOf/Object.setPrototypeOf

let user = {
    name: "John",
    surname: "Smith",
    
    set fullName(value) {
        [this.name, this.surname] = value.split(" ");
    },
    
    get fullName() {
        return `${this.name} ${this.surname}`;
    }
}

let admin = {
    __proto__: user,
    isAdmin: true
}

alert(admin.fullName);
// => John Smith

admin.fullName = "Alice Cooper";

alert(admin.fullName);// => Alice Cooper

alert(user.fullName);// => John Smith

Khi dùng prototype, không trực tiếp thay đổi property ở object cha từ object con, việc này cần thông qua một hàm setter để đảm bảo dữ liệu sẽ độc lập trên từng object.

Như ví dụ trên, this lúc này đang trỏ đến object phía trước dấu ., nên dữ liệu hoàn toàn độc lập, trong khi các phương thức thì share với nhau

Vòng lặp for...in

let animal = {
  eats: true
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

alert(Object.keys(rabbit));// jumps

for(let prop in rabbit) alert(prop); // jumps, then eats

Nếu không muốn chạy qua các property kề thừa qua __prototype__, sử dụng obj.hasOwnProperty(key) để xác định 1 property của được kế thừa hay không

for(let prop in rabbit) {
  let isOwn = rabbit.hasOwnProperty(prop);
  if (isOwn) {
    alert(`Our: ${prop}`);
    // Our: jumps
  } else {
    alert(`Inherited: ${prop}`);
    // Inherited: eats
  }
}

Một điều thú vị, nếu để ý chúng ta không hề khai báo rabbit.hasOwnProperty, vậy nó từ đâu mà có? và nó cũng không xuất hiện bên trong vòng lặp for...in?

Đây là một property kế thừa từ Object chúa, và nó đã được khai báo decriptor với giá trị enumerable: false

Đọc lại bài decriptor

Tóm tắt

  • Mỗi object sẽ chứa một property đặc biệt [[Prototype]], giá trị là null, hoặc trỏ đến một object khác
  • Sử dụng obj.__proto__ để truy cập
  • this luôn trỏ đến obj hiện tại thay vì prototype object
  • for..in sẽ chạy qua tất cả property chính chủ và property được kế thừa

Bài viết gốc được đăng tải tại vuilaptrinh.com

Có thể bạn quan tâm:

Xem thêm Việc làm IT hấp dẫn trên TopDev