proto-prototype-constructor
__proto__、prototype、constructor 三者关系拆解。
#type / concept
#status / growing
#resource / javascript
#resource / ecmascript
[!info] related notes
- 所属 MOC: ecmascript-object-oriented, ecmascript-prototypes
- 前置概念: js原型和原型链, js构造函数
- 并列概念: js-new, js-class
- 关系笔记: [[ecmascript-object-object总结]]
__proto__、prototype、constructor 三者关系
一句话定义
__proto__是每个实例指向其构造函数prototype的链接(原型链的连接线)prototype是构造函数独有的属性,存放所有实例共享的方法constructor是prototype上的属性,指回构造函数本身
核心机制 / 工作原理
三者的关系图
function Foo() {}
foo = new Foo()
foo.__proto__ -----> Foo.prototype
|
| .constructor
|
v
Foo
核心等式
function Foo() {}
const foo = new Foo();
// 1. 实例的 __proto__ 指向构造函数的 prototype
foo.__proto__ === Foo.prototype; // true
// 2. prototype 上的 constructor 指回构造函数
Foo.prototype.constructor === Foo; // true
// 3. 由此推导
foo.__proto__.constructor === Foo; // true
foo.constructor === Foo; // true(沿原型链查找到)
prototype 链的逐级查找
foo.hasOwnProperty('toString') // false
// 查找过程:
// 1. foo 自身没有 toString
// 2. foo.__proto__ (= Foo.prototype) 没有 toString
// 3. Foo.prototype.__proto__ (= Object.prototype) 找到了 toString
完整链:foo -> Foo.prototype -> Object.prototype -> null
constructor 不是”自带的”
constructor 是在创建函数时,JS 引擎自动在 Foo.prototype 上设置的。但如果手动覆盖了 prototype:
Foo.prototype = {}; // 覆盖了整个 prototype
// 此时 Foo.prototype.constructor 丢失,变成 Object
// 修复:Foo.prototype.constructor = Foo;
最小例子
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return 'Hi, ' + this.name;
};
const p = new Person('Alice');
// 验证三者关系
p.__proto__ === Person.prototype; // true
Person.prototype.constructor === Person; // true
p.__proto__.constructor === Person; // true
// 原型链查找
p.greet(); // 'Hi, Alice' — 从 Person.prototype 上找到
p.constructor; // Person — 从 Person.prototype.constructor 找到
p instanceof Person; // true
边界与常见误解
- “每个对象都有 prototype” — 错。只有函数才有
prototype属性。普通对象只有__proto__。 - “
__proto__是标准” —__proto__是非标准的访问方式(虽然所有浏览器都支持)。标准方式是Object.getPrototypeOf(obj)。 - “constructor 是只读的” — 不是。
constructor可以被覆盖,覆盖后instance.constructor会指向错误的构造函数。 - 覆盖
prototype后忘记修复constructor— 这是经典 bug。Person.prototype = {}之后,p.constructor变成Object而不是Person。 - 箭头函数没有
prototype— 箭头函数不能作为构造函数,没有prototype属性。
related notes
- js原型和原型链
- js构造函数
- new 操作符
- ES6 class
- ES5 vs ES6 继承
- [[ecmascript-object-object总结]]