Khóa học javascript

Các phương thức với prototype trong JavaScript

0 phút đọc

Trong bài viết Prototype là gì? Prototype trong JavaScript, mình nói rằng __proto__ đã lỗi thời và bạn nên dùng các phương thức Object.getPrototypeOfObject.setPrototypeOf để thay thế.

Vì vậy, bài viết này mình sẽ tập trung vào tìm hiểu về các phương thức với prototype trong JavaScript.

Phương thức của prototype

Các phương thức của prototype trong JavaScript bao gồm:

Ví dụ về các phương thức của prototype:

js Copy
let animal = {
  eats: true,
};

// tạo đối tượng với với prototype là animal
let rabbit = Object.create(animal);
console.log(rabbit.eats); // true

console.log(Object.getPrototypeOf(rabbit) === animal); // true
Object.setPrototypeOf(rabbit, {}); // thay đổi prototype của rabbit thành {}

Đối tượng mô tả thuộc tính descriptors giống như mình đã giới thiệu trong bài viết về writable, enumerable và configurable.

Bạn có thể sử dụng Object.create để clone object thay vì cách sử dụng vòng lặp for...in:

js Copy
let clone = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);

Câu lệnh trên copy object một cách hoàn toàn, bao gồm tất cả các thuộc tính (enumerable và non-enumberable), getters/setters,... với giá trị chính xác của [[Prototype]].

Object thuần

Như bạn đã biết, object thuần thường dùng để lưu các cặp key-value, với key là string hoặc symbol. Và một điều thú vị, key có thể là bất kỳ string nào, ngoại trừ "__proto__", ví dụ:

js Copy
let obj = {};
let key = "__proto__";

obj[key] = "some value";
console.log(obj[key].toString()); // "[object Object]" không phải "some value".

Trong ví dụ trên, mình muốn lưu trữ cặp key-value với giá trị của key là "__proto__". Tuy nhiên, key này bị bỏ qua. Bởi vì, "__proto__" là một thuộc tính đặc biệt có giá trị null hoặc một object.

Làm sao để giải quyết vấn đề này?

Cách 1: bạn có thể chuyển qua sử dụng Map thay vì sử dụng object thuần như sau:

js Copy
let obj = new Map();
let key = "__proto__";

obj.set(key, "some value");
console.log(obj.get(key).toString()); // "some value"

Cách 2: sử dụng Object.create(null), ví dụ:

js Copy
let obj = Object.create(null);
let key = "__proto__";

obj[key] = "some value";
console.log(obj[key].toString()); // "some value"

Bởi vì, __proto__ không phải một thuộc tính trong object, mà chỉ là getter/setter cho [[Prototype]].

Khi gọi Object.create(null), thực chất là bạn đang tạo một object không có [[Prototype]]. Do đó, __proto__ cũng không trở thành getter/setter.

Vì vậy, __proto__ có thể trở thành thuộc tính bình thường trong object như cách làm trên.

Tổng kết

Các phương thức với prototype trong JavaScript là:

__proto__ thực chất là getter/setter của [[Prototype]] trong object. Vì vậy, bạn không thể lưu key-value với giá trị của key là __proto__.

Để giải quyết vấn đề trên, bạn có thể sử dụng Map thay vì object thuần, hoặc dùng Object.create(null) để tạo ra object không có prototype.

Ngoài ra, Object.create còn được dùng để clone object như sau:

js Copy
let clone = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);

Các phương thức hữu ích khác:

Thực hành

Bài 1

Cho đoạn code sau:

js Copy
let dictionary = Object.create(null);

// Thêm phương thức toString vào dictionary tại đây

// thêm data vào dictionary
dictionary.apple = "Apple";
dictionary.__proto__ = "test"; // __proto__ là một thuộc tính thường

// chỉ apple và __proto__ xuất hiện trong vòng lặp
for (let key in dictionary) {
  console.log(key); // "apple" rồi đến "__proto__"
}

// gọi dictionary.toString()
console.log(dictionary.toString()); // "apple,__proto__"

Hãy thêm phương thức toString vào dictionary để thỏa mãn đoạn code trên?

Xem đáp án

js Copy
let dictionary = Object.create(null);

// Thêm phương thức toString vào dictionary tại đây
Object.defineProperty(dictionary, "toString", {
  value: function () {
    return Object.keys(this).join(",");
  },
  enumberable: false,
});
// thêm data vào dictionary
dictionary.apple = "Apple";
dictionary.__proto__ = "test"; // __proto__ là một thuộc tính thường

// chỉ apple và __proto__ xuất hiện trong vòng lặp
for (let key in dictionary) {
  console.log(key); // "apple" rồi đến "__proto__"
}

// gọi dictionary.toString()
console.log(dictionary.toString()); // "apple,__proto__"

Bài 2

Cho đoạn code sau:

js Copy
function Rabbit(name) {
  this.name = name;
}
Rabbit.prototype.sayHi = function () {
  console.log(this.name);
};

let rabbit = new Rabbit("Rabbit");

Các câu lệnh sau có giống nhau hay không?

js Copy
rabbit.sayHi(); // (1)
Rabbit.prototype.sayHi(); // (2)
Object.getPrototypeOf(rabbit).sayHi(); // (3)
rabbit.__proto__.sayHi(); // (4)

Xem đáp án

Kết quả

(1) - Rabbit
(2) - undefined
(3) - undefined
(4) - undefined

Câu lệnh (1) thì this=rabbit, bởi vì rabbit là object đứng trước ., nên đáp án đúng như trên.

Các câu lệnh (2), (3), (4) thì this=Rabbit.prototype. Mà trong Rabbit.prototype không có thuộc tính name.

Tham khảo: Prototype methods, objects without __proto__

Avatar
Được viết bởi

Admin Team

Gợi ý câu hỏi phỏng vấn

Không có dữ liệu

Không có dữ liệu

Gợi ý bài viết
Không có dữ liệu

Không có dữ liệu

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào