对象有个特殊的隐藏属性 [[Prototype]]
__proto__
与 [[Prototype]]
属性 [[Prototype]]
是内部的而且是隐藏的,但是有设置此属性的方法。
例如,__proto__
let animal = {
eats: true
};
let jiaoshou = {
jumps: true
}
jiaoshou.__proto__ = animal; // 将 animal 设为 jiaoshou 的原型
现在,如果从 jiaoshou 读取一个它没有的属性,JS 会顺着 [[Prototype]]
到 animal 中寻找:
jiaoshou.eats => true
animal['f'] = function() {console.log('f**k')}
jiaoshou.f() => "fuck"
我们可以说,animal 是 jiaoshou 的原型。或者说,jiaoshou 的原型是从 animal 继承来的。animal 中的属性和方法自动地在 jiaoshou 中可用,这就是继承。
这种继承方式有书写限制:
- 引用不能形成闭环。会报
TypeError: Cyclic __proto__ value
__proto__
只能是 null 或者对象,其他的值不生效。
NOTE,
[[Prototype]]
只有一个。一个对象不能同时从两个对象获得直接继承。
区别
__proto__
与[[Prototype]]
不一样,__proto__
是[[Prototype]]
的 getter 和 setter。__proto__
属性是历史遗留的老家伙。可以使用Object.get/set/PrototypeOf
替代。
this 的指向
在一个方法调用中, this 始终是点符号 .
前面的对象。
let user = {
name: "John",
surname: "Smith",
firends: [1, 2],
set fullName(value) {
[this.name, this.surname] = value.split(" ");
},
get fullName() {
return `${this.name} ${this.surname}`;
},
get fri() {
return this.firends;
},
set fri(f) {
this.firends.push(f);
}
};
let admin = {
__proto__: user,
isAdmin: true
};
console.log(admin.fri); // [1,2]
console.log(admin.fri == user.fri); //true
admin.fri = 3;
console.log(admin.fri, user.fri); // [1, 2, 3] [1, 2, 3]
console.log(admin.fri == user.fri); //true
admin.hasOwnProperty("surname") // false
console.log(admin.fullName); // John Smith
admin.fullName = "lu you";
console.log(admin.fullName); // "lu you"
console.log(user.fullName); // "John Smith"
admin.hasOwnProperty("surname") // true
当我们调用 fullName 的 setter 时,写入了 this,所以会将其存储到这个 this 对应的对象,也就是 admin 中,让 admin 有了自己的 name 和 surname 属性。
for in 遍历
for in 遍历会遍历可枚举属性。包括继承的可枚举属性。
使用 hadOwnProperty
可以过滤掉继承来的属性。
练习
let animal = { jumps: null }; let rabbit = { __proto__: animal, jumps: true }; alert( rabbit.jumps ); // ? (1) delete rabbit.jumps; alert( rabbit.jumps ); // ? (2) delete animal.jumps; alert( rabbit.jumps ); // ? (3)
2.
let head = {
glasses: 1
};
let pockets = {
money: 2000
};
pockets.__proto__ = head;
通过 pockets.glasses
或 head.glasses
获取 glasses
,哪个更快?
- 为什么两只仓鼠都饱了?
我们有两只仓鼠:speedy
和lazy
都继承自普通的hamster
对象。
当我们喂其中一只的时候,另一只也吃饱了。为什么?如何修复它?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
---
答案:
1. (1)true,直接访问自己的 jumps 属性;(2)null,访问 animal 的(3)undefined,找不到此属性
2. 在现代引擎中,从性能的角度来看,我们是从对象还是从原型链获取属性都是没区别的。因为访问一次后就缓存了。
3. 因为 `speedy.stomach == lazy.stomach => true`,都指向他们的原型 --hamster 对象的 `stomach 属性 `。调用 `speedy.eat` 方法时,改动的也是 `stomach 属性 `。因此,所有的仓鼠共享了同一个胃!
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论。