8.1 原型继承
for..in 循环也会迭代继承的属性。
let animal = {
eats: true,
};
let rabbit = {
jumps: true,
__proto__: animal,
};
// Object.keys 只返回自己的 key
alert(Object.keys(rabbit)); // jumps
// for..in 会遍历自己以及继承的键
for (let prop in rabbit) alert(prop); // jumps,然后是 eats
几乎所有其他键/值获取方法,例如 Object.keys 和 Object.values 等,都会忽略继承的属性。
它们只会对对象自身进行操作。不考虑 继承自原型的属性。
在现代引擎中,从性能的角度来看,我们是从对象还是从原型链获取属性都是没区别的。它们(引擎)会记住在哪里找到的该属性,并在下一次请求中重用它。
// this.stomach= 不会执行对 stomach 的查找。该值会被直接写入 this 对象。
// this.stomach.push(food); 会
let hamster = {
stomach: [],
eat(food) {
this.stomach.push(food);
},
};
let speedy = {
__proto__: hamster,
};
let lazy = {
__proto__: hamster,
};
// 这只仓鼠找到了食物
speedy.eat("apple");
alert(speedy.stomach); // apple
// 这只仓鼠也找到了食物,为什么?请修复它。
alert(lazy.stomach); // apple
8.2 F.prototype
F.prototype 属性仅在 new F 被调用时使用,它为新对象的 [[Prototype]] 赋值。
let animal = {
eats: true,
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert(rabbit.eats); // true
每个函数都有 “prototype” 属性,即使我们没有提供它。
默认的 “prototype” 是一个只有属性 constructor 的对象,属性 constructor 指向函数自身。
function Rabbit(name) {
this.name = name;
alert(name);
}
let rabbit = new Rabbit("White Rabbit");
let rabbit2 = new rabbit.constructor("Black Rabbit");
注意,当 prototype 被修改,constructor 可能就不存在了
8.4 现代原型方法
设置和直接访问原型的现代方法有:
Object.create(proto, [descriptors]) —— 利用给定的 proto 作为 [[Prototype]](可以是 null)和可选的属性描述来创建一个空对象。
Object.getPrototypeOf(obj) —— 返回对象 obj 的 [[Prototype]](与 proto 的 getter 相同)。
Object.setPrototypeOf(obj, proto) —— 将对象 obj 的 [[Prototype]] 设置为 proto(与 proto 的 setter 相同)。
let animal = {
eats: true,
};
// 创建一个以 animal 为原型的新对象
let rabbit = Object.create(animal);
alert(rabbit.eats); // true
alert(Object.getPrototypeOf(rabbit) === animal); // true
Object.setPrototypeOf(rabbit, {}); // 将 rabbit 的原型修改为 {}