原型与原型链

JavaScript 常被描述为一种基于原型的语言——每个对象拥有一个原型对象。

当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

准确地说,这些属性和方法定义在 Object 的构造器函数(constructor functions)之上的 prototype 属性上,而非实例对象本身。

原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链(prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

在对象实例和它的构造器之间建立一个链接(它是 __proto__ 属性,是从构造函数的 prototype 属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。

一段直观示例

function Person(name) {
  this.name = name;
}
Person.prototype.sayHi = function () {
  console.log('hi, ' + this.name);
};

const p = new Person('Tom');
p.sayHi(); // hi, Tom

// 关键链路:
// p -> Person.prototype -> Object.prototype -> null
console.log(p.__proto__ === Person.prototype);             // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__);                   // null

补充:几个常被问到的细节点:

  • obj.__proto__ 只是 Object.getPrototypeOf(obj) 的 getter,生产环境推荐使用后者。
  • 所有函数的 __proto__ 都指向 Function.prototype,所有数组的 __proto__ 都指向 Array.prototype。
  • Object.create(proto) 可以创建一个以 proto 为原型的对象,常用来做原型式继承。
  • 修改 Array.prototype 等内置对象原型是危险的,会影响所有同类型实例(慎用,多见于 polyfill)。

来源整理自:vue3js.cn 面试官系列