function F() {};
F.prototype = "red";
o = new F();
Run Code Online (Sandbox Code Playgroud)
我发现上面的代码片段很有趣,当在控制台中复制时,它显示因为对象的[[prototype]]插槽不能是对象或以外的任何东西null,"red"是不可接受的 &o的[[prototype]]插槽似乎只是默认为Object.prototype. 我可以确认这是当它们的构造函数.prototype不是对象时实例所做的事情吗?
(开放的观点认为这是一个不切实际的问题,因为我们为什么要将构造函数重新初始化为.prototype对象以外的东西......但我想我无论如何都会问只是为了检查)。
以下是基于 ECMAScript 2020 规范的分析。
首先,很明显该prototype属性可以获得分配给它的任何值。第19.2.4.3节原型没有提到具体限制:
除非另有说明,否则“prototype”属性的值用于初始化该函数作为构造函数调用时创建的对象的 [[Prototype]] 内部槽。
此属性具有属性 { [[Writable]]: true , [[Enumerable]]: false , [[Configurable]]: false }。
通过构造函数创建对象的过程在第9.2.2节[[Construct]] 中指定。关键操作在步骤 5 中描述:
OrdinaryCreateFromConstructor( newTarget , "%Object.prototype%" )。
指定为第二个参数的名称很有趣。从第19.1.2.19节Object.prototype 中我们了解到这实际上是它的名字Object.prototype(毫不奇怪)。
在9.1.13 OrdinaryCreateFromConstructor节中,我们看到该参数称为IntrinsicDefaultProto,其过程的第 2 步依赖于9.1.14 GetPrototypeFromConstructor,我们最终在第 3 步和第 4 步中获得了核心:
- 让原型是?获取(构造函数,“原型”)。
- 如果 Type( proto ) 不是 Object,则
- 让境界是?GetFunctionRealm(构造函数)。
- 将proto设置为Realm的名为internalDefaultProto的内部对象。
所以用通俗的英语来说,这意味着如果prototype构造函数的属性不是对象,那么Object.prototype将用于定义新构造对象的原型对象。
这也意味着如果prototype属性有 value null,那么构造的对象仍然会得到Object.prototype而不是null原型。
要制作对象的原型null,您需要使用Object.create.
以上可以用一个小片段来演示。
function F() {};
// Test 1
F.prototype = "red";
console.log("F.prototype: " + F.prototype); // "red" indeed.
let o = new F;
console.log("Is proto Object.prototype?",
Object.getPrototypeOf(o) === Object.prototype); // true
// Test 2
F.prototype = null;
console.log("F.prototype: " + F.prototype); // null indeed.
o = new F;
console.log("Is proto Object.prototype?",
Object.getPrototypeOf(o) === Object.prototype); // true
// Test 3
o = Object.create(null);
console.log("Is proto of Object.create(null) null?",
Object.getPrototypeOf(o) === null); // trueRun Code Online (Sandbox Code Playgroud)