理解 JavaScript 中的原型对象

时间:2022-03-29 14:06:21

关于原型对象

  1. 一个新函数被创建以后,就会根据一组特定的规则为该函数创建一个 prototype 属性。
  2. 新函数创建时还会创建一个包含 constructor 属性的原型对象。
  3. 新函数的 prototype 属性指向原型对象;原型对象的 constructor 属性指向新函数。
  4. 调用新函数创建的每个实例都包含一个不能直接访问的内部属性 [[Prototype]] 指向原型对象。

从上面的关系可以看出,原型对象是连接实例与构造函数之间的桥梁。

没有标准的方式直接访问实例的内部属性 [[Prototype]](FireFox、 Safari 和 Chrome 在每个对象上都支持一个属性 __proto__),但存在可以确定实例与原型对象之间关系的方法。

  • Object.prototype.isPrototypeOf(obj):如果 obj 的内部属性 [[Prototype]] 指向调用 isPrototypeOf() 方法的对象则返回 true
  • Object.getPrototypeOf(obj):返回 obj[[Prototype]] 的值(即原型对象的引用)。

关于原型链

每当代码读取某个对象的某个属性时,都会对该属性进行搜索。搜索首先从对象本身开始,如果在对象中找到了所搜索的属性,则返回该属性的值;如果没有找到,则继续搜索该对象的 [[Prototype]] 指针指向的原型对象。这可以看做是一个递归的过程。

// 抽象化描述对象属性的搜索过程
function search (obj, prop) {
var proto = Object.getPrototypeOf(obj);

if (obj.hasOwnProperty(prop)) {
return obj.getOwnPropertyDescriptor(prop);
} else if (proto) {
search(proto, prop);
} else {
return undefined;
}
}

上面的函数描述了对象属性的搜索过程。因为一个对象的原型对象也拥有自己的原型对象,这种层层递进的关系就构成了原型链。事实上,所有对象的原型链的末端都是 Object.prototype

var o = {};

console.log(Object.getPrototypeOf(o) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null

函数作为对象也包含内部属性 [[Prototype]],但函数的 prototype 属性与内部属性 [[Prototype]] 并不是同一个概念。函数的 prototype 属性指的是通过该函数创建的实例的原型对象;函数的 [[Prototype]] 属性指的是该函数作为一个对象其内部所指向的原型对象。

例如,所有的对象都是 Object 的实例,因此 Object.prototype 是所有对象原型链的末端;而 Object 本身是 Function 的实例,它的原型对象指向 Function.prototype

console.log(typeof Object);     // function
console.log(Object.getPrototypeOf(Object) === Object.prototype); // false
console.log(Object instanceof Function); // true
console.log(Object.getPrototypeOf(Object) === Function.prototype); // true
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype); // true