对于原型的理解

171 阅读2分钟

真正的原型叫[[Prototype]]

  • __proto__ 是 [[Prototype]] 的因历史原因而留下来的 getter/setter。__proto__ 属性有点过时了,但是能用。

现在用Object.getPrototypeOf/Object.setPrototypeOf 来取代 __proto__ 去 get/set 原型

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*

有一个内建方法 obj.hasOwnProperty(key):如果 obj 具有自己的(非继承的)名为 key 的属性,则返回 true

image.png

hasOwnProperty也是继承自Object.prototype的,奇怪的是for..in 循环会列出继承的属性,但没列出来 hasOwnProperty

因为:它是不可枚举的。就像 Object.prototype 的其他属性,hasOwnProperty 有 enumerable:false 标志。并且 for..in 只会列出可枚举的属性。这就是为什么它和其余的 Object.prototype 属性都未被列出。

  • 通过构造函数创建的对象设置 [[Prototype]] 的方法: F.prototype 属性在 new ‘ 的时候可以指定新对象的原型 F.prototype 的值要么是一个对象,要么就是 null`:其他值都不起作用。

"prototype" 属性仅在设置了一个构造函数(constructor function),并通过 new 调用时,才具有这种特殊的影响。

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

image.png

原生的原型是可以被修改的。

例如,我们向 String.prototype 中添加一个方法,这个方法将对所有的字符串都是可用的:

String.prototype.show = function() { alert(this); };
"BOOM!".show(); // BOOM!

现代的方法

let animal = { eats: true }; // 创建一个以 animal 为原型的新对象 
let rabbit = Object.create(animal);
alert(rabbit.eats); // true 
alert(Object.getPrototypeOf(rabbit) === animal); // true 
Object.setPrototypeOf(rabbit, {}); // 将 rabbit 的原型修改为 {}

Object.create 有一个可选的第二参数:属性描述器。我们可以在此处为新对象提供额外的属性,就像这样

let animal = { eats: true }; 
let rabbit = Object.create(animal, { jumps: { value: true } }); 
alert(rabbit.jumps); // true