Object.prototype.constructor 您所在的位置:网站首页 实例对象是什么意思 Object.prototype.constructor

Object.prototype.constructor

2024-01-15 08:24| 来源: 网络整理| 查看: 265

每个构造函数都有一个 prototype 属性,当通过 new 运算符调用时,该属性将成为实例的 [[Prototype]]。因此,ConstructorFunction.prototype.constructor 将成为实例的 [[Prototype]] 上的属性,如前面所述。

然而,如果对 ConstructorFunction.prototype 重新赋值,constructor 属性将丢失。例如,以下是创建继承模式的常见方式:

jsfunction Parent() { // … } Parent.prototype.parentMethod = function () {}; function Child() { Parent.call(this); // 确保所有内容都已正确初始化 } // 将 Child.prototype 的 [[Prototype]] 指向 Parent.prototype Child.prototype = Object.create(Parent.prototype);

由于重新赋值了 Child.prototype,Child 实例的 constructor 将是 Parent。

通常情况下,这不是什么大问题——JavaScript 几乎从不读取对象的 constructor 属性。唯一的例外是在使用 @@species 创建类的新实例时,但这种情况很少见,并且你应该使用 extends 语法来子类化内置对象。

然而,在某些调用使用 constructor 从实例中访问原始类时,确保 Child.prototype.constructor 总是指向 Child 本身非常重要。考虑这种情况:对象具有 create() 方法来创建自身。

jsfunction Parent() { // … } function CreatedConstructor() { Parent.call(this); } CreatedConstructor.prototype = Object.create(Parent.prototype); CreatedConstructor.prototype.create = function () { return new this.constructor(); }; new CreatedConstructor().create().create(); // TypeError: new CreatedConstructor().create().create is undefined,因为 constructor === Parent

在上面的示例中,会抛出一个异常,因为 constructor 链接到 Parent。为了避免这种情况,只需将其赋值为你将要使用的必要构造函数即可。

jsfunction Parent() { // … } function CreatedConstructor() { // … } CreatedConstructor.prototype = Object.create(Parent.prototype, { // 将原始构造函数返回给 Child constructor: { value: CreatedConstructor, enumerable: false, // 使其不可枚举,这样它就不会出现在 `for...in` 循环中 writable: true, configurable: true, }, }); CreatedConstructor.prototype.create = function () { return new this.constructor(); }; new CreatedConstructor().create().create(); // 跑起来没毛病

请注意,当手动添加 constructor 属性时,将属性设置为不可枚举非常重要,这将确保 constructor 就不会在 for...in 循环中被访问——尽管通常情况下不会被访问。

如果上面的代码看起来太死板,你也可以考虑使用 Object.setPrototypeOf() 来操作原型链。

jsfunction Parent() { // … } function CreatedConstructor() { // … } Object.setPrototypeOf(CreatedConstructor.prototype, Parent.prototype); CreatedConstructor.prototype.create = function () { return new this.constructor(); }; new CreatedConstructor().create().create(); // 在不重新创建 constructor 属性的情况下仍然有效

Object.setPrototypeOf() 存在潜在的性能缺陷,因为所有先前创建的涉及该原型链的对象都必须重新编译;但是,如果上述初始化代码发生在 Parent 或 CreatedConstructor 构造之前,其影响应该是很小的。

接下来,看另外一个相关示例。

jsfunction ParentWithStatic() {} ParentWithStatic.startPosition = { x: 0, y: 0 }; // 静态成员属性 ParentWithStatic.getStartPosition = function () { return this.startPosition; }; function Child(x, y) { this.position = { x, y }; } Child.prototype = Object.create(ParentWithStatic.prototype, { // 将原始构造函数返回给 Child constructor: { value: Child, enumerable: false, writable: true, configurable: true, }, }); Child.prototype.getOffsetByInitialPosition = function () { const position = this.position; // 使用 `this.constructor`,以期 `getStartPosition` 存在于一个静态方法中。 const startPosition = this.constructor.getStartPosition(); return { offsetX: startPosition.x - position.x, offsetY: startPosition.y - position.y, }; }; new Child(1, 1).getOffsetByInitialPosition(); // Error: this.constructor.getStartPosition is undefined, // 因为构造函数是 Child,它没有 getStartPosition 静态方法

如果想要保证示例正常运行,我们需要让 Parent 作为构造函数,或给 Child 的构造分配静态属性:

js// … Object.assign(Child, ParentWithStatic); // 注意,在创建 Child 的原型前我们先分配它的值 Child.prototype = Object.create(ParentWithStatic.prototype, { // 将原始构造函数返回给 Child constructor: { value: Child, enumerable: false, writable: true, configurable: true, }, }); // …

但更好的方法是,我们可以使构造函数本身相互继承,就像类的 extends 一样。

jsfunction ParentWithStatic() {} ParentWithStatic.startPosition = { x: 0, y: 0 }; // 静态成员属性 ParentWithStatic.getStartPosition = function () { return this.startPosition; }; function Child(x, y) { this.position = { x, y }; } // 正确地创建继承关系! Object.setPrototypeOf(Child.prototype, ParentWithStatic.prototype); Object.setPrototypeOf(Child, ParentWithStatic); Child.prototype.getOffsetByInitialPosition = function () { const position = this.position; const startPosition = this.constructor.getStartPosition(); return { offsetX: startPosition.x - position.x, offsetY: startPosition.y - position.y, }; }; console.log(new Child(1, 1).getOffsetByInitialPosition()); // { offsetX: -1, offsetY: -1 }

再次强调,使用 Object.setPrototypeOf() 可能会对性能产生不利影响,因此请确保它仅在必要时使用,并在构造函数声明后立即使用,并在创建任何实例之前使用,以避免对象被“污染”。

备注: 设置或更新构造函数可能会导致结果不同且令人困惑的结果。为了防止它,只需在特定情况下定义 constructor。多数情况,不使用 constructor,并且不需要重新对其赋值。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有