B.__proto__.__proto__ 确实是 Function.prototype
,但首先它的原型是 A ,其原型的原型才是函数原型。因为定义在 A 上的静态方法 B 也要继承。
更新:
每一个对象都有原型,但是对象的原型并不一定是对象的构造函数的 prototype 属性。
我在这里先声明一下几个术语,前端届对原型的称呼一直比较混乱:
说的有点多,回到你的问题:
每一个对象都有原型,但是对象的原型并不一定是对象的构造函数的 prototype 属性。
且不说 constructor 属性并不是一个锁死的属性,JS 中的有些对象也并不一定存在一个构造器。
var father = {money: 1000,house:'big house'};
// father 对象使用对象字面量定义,本质上和 new Object 一回事
// 所以 father 是用 Object 构造的
father.constructor === Object // 没问题
var son = Object.create(father);
son.money // 这里我们说 son 继承了 father 的所有属性
Object.getPrototypeOf(son) === father; // 毛得问题吧
son.constructor // 这玩意儿会是什么?
son.constructor === father.constructor; // 嗯,来自于父亲
如上代码展示,其实 JS 中对象的构造器不是在所有场合都合理且有意义。构造函数这个概念在基于 prototype 继承的体系里其实是不需要的,它是 JS 模拟基于类的继承加入的东西。
那么题主的问题到底出现在哪里呢?出现在这里:
class
是语法糖,本质上还是个函数,因此B
实际上是个函数,可以看作通过new Function
创建出来的Function
对象实例
class 是语法糖没错,本质是函数也没错,错在 class B 并不能看作是 new Function 构造出来的,class A 可以,但是 B 不行。因为 B extends A,B 是通过 A 构造出来的。你忘了,JS 继承的本质是对象继承。 class B extends A 这里隐含了两个 prototype 继承。 B.prototype 继承了 A.prototype, 同时 B 继承了 A。
class A {
static p = 1;
}
class B extends A{}
B.p; // 1
Object.getPrototypeOf(B.prototype) === A.prototype; // true
Object.getPrototypeOf(B) === A; // true
//或者说用非标准属性表示 B.__proto__ === A
因为静态属性也是需要继承的,所以 B 并不是直接通过 new Function 构造出来的,B 是通过 A 作为原型构造出来的,即 B = Object.create(A); 这样 B 才能获得 A 上定义的静态属性和方法,才符合基于 class 的继承的表现。
所以,B.constructor 在这里并没有很符合实际的意义,并不存在一个函数它把 B 构造了出来。从原型的角度看待就不存在这种问题,B 用了 A 当原型,A 用了 Function.prototype 当原型,仅此而已。
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…