原型对象和构造函数有何关系?
面试速答(30 秒版 TL;DR)
- 关系一:构造函数的
prototype属性指向一个“原型对象”,它会成为new出来实例的[[Prototype]]。 - 关系二:原型对象上通常有个
constructor指回构造函数(默认如此,但可被你改坏)。 - 关系三:把方法挂到
Ctor.prototype上,能让所有实例共享一份函数实现,避免每个实例都拷贝一套方法。
关键机制:new 只关心 Ctor.prototype
new Ctor() 的核心连接动作是:
Object.getPrototypeOf(instance) === Ctor.prototype
所以你可以把“原型对象”讲成:
- “
Ctor.prototype是实例原型链的第一个跳板(第一站)。”
示例 1:为什么把方法放在 prototype 上?
function User(name) {
this.name = name;
}
User.prototype.sayHi = function () {
return `hi, ${this.name}`;
};
const a = new User("a");
const b = new User("b");
console.log(a.sayHi()); // hi, a
console.log(a.sayHi === b.sayHi); // true (共享同一个函数)
如果把 sayHi 写在构造函数里(this.sayHi = ...),那每次 new 都会创建一份新函数,不利于内存与一致性。
示例 2:为什么“替换 prototype”经常要补 constructor?
function Foo() {}
Foo.prototype = { x: 1 };
console.log(Foo.prototype.constructor === Foo); // false
console.log(Foo.prototype.constructor === Object); // true (通常会变成 Object)
修复方式(让 constructor 指回去,且保持不可枚举):
function Foo() {}
Foo.prototype = { x: 1 };
Object.defineProperty(Foo.prototype, "constructor", {
value: Foo,
writable: true,
configurable: true,
enumerable: false,
});
图示:一正一反两条指针
常见追问
Q1:所有函数都有 prototype 吗?
不是。箭头函数没有 prototype,也不能作为构造函数使用。
Q2:class 和原型是什么关系?
class 只是语法糖:class C { m() {} } 的实例方法本质仍在 C.prototype.m 上;面试时用“底层仍是原型链委托”即可。
易错点/坑
- 别把
Ctor.prototype(原型对象)和Ctor[[Prototype]](函数对象自己的原型)混为一谈。 Object.setPrototypeOf动态改原型会影响引擎优化,通常不推荐在热路径使用。
速记要点(可背诵)
Ctor.prototype决定实例“往上找”的第一站。prototype.constructor只是一个普通属性,可能被你替换原型时破坏。