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ó đó
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
for...in
Vòng lặp 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
Object.prototype
Xét qua ví dụ
let obj = {}
console.log( obj )
// => "[object Object]"
Bạn có bao giờ thắc mắc tại sao lại có kết quả [object Object]
? obj
là một object rỗng mà?
Thật ra, obj = {}
sẽ tương đương với obj = new Object()
, tức là, chúng ta khởi tạo một object thông qua việc gọi một constructor function, và nó sẽ kế thừa tất cả property, phương thức từ Object
, như toString
, [[Prototype]]
Khi gọi obj.toString()
có nghĩa là chúng ta đang gọi đến phương thức Object.prototype.toString
let obj = {}
console.log(obj.__proto__ === Object.prototype)
// => true
console.log(obj.toString === obj.__proto__.toString)
// => true
console.log(obj.toString === Object.prototype.toString)
// => true
Tất cả những object khác như
Array
,Date
,Function
đều kế thừa từObject
let arr = [1, 2, 3];
alert( arr.__proto__ === Array.prototype );
// true
alert( arr.__proto__.__proto__ === Object.prototype );
// true
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 objectfor..in
sẽ chạy qua tất cả property chính chủ và property được kế thừa- Tất cả những object build-in (như Array, Object, Date) đều lưu các phương thức bên trong property
prototype
(Array.prototype, Object.prototype, Date.prototype)
Javascript.info
Initializing...