“new”关键字是在构造对象时自动设置“constructor”属性的唯一方法吗

Is the `new` keyword the only way to automatically set the `constructor` property when constructing objects?

本文关键字:constructor 设置 属性 唯一 方法 关键字 new 对象      更新时间:2023-09-26

我目前正在使用Object.create()来构造这样的对象:

const Tab = ({id, windowId}) => Object.assign(Object.create(Tab.prototype), {id, windowId})
Tab.prototype = {
  constructor: Tab,
  toString: function() {
    return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
  }
}

其中一个目标是避免使用new关键字,这样我就可以使用构造函数,例如:[{id: 1, windowId: 2}].map(Tab)(与String等原生构造函数相同;例如,[1,2,3].map(String)可以工作)。问题是,必须手动定义构造函数属性是不好的,所以有没有办法绕过它,让构造函数自动设置,比如使用new关键字,同时仍然使用Object.create()

更新基于答案的固定版本:

const Tab = function({id, windowId}) {
  return Object.assign(Object.create(Tab.prototype), {id, windowId})
}
Object.assign(Tab.prototype, {
  toString: function() {
    return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
  }
})

new运算符不会创建constructor属性。它只是调用[[Construct]]内部方法。

默认情况下,实例没有任何constructor属性。他们只从他们的[[Prototype]]继承它,这是构造函数的prototype

创建函数时,prototypeconstructor属性只创建一次。

如果你想能够将构造函数作为函数调用,你可以使用

function Constructor() {
  if(this instanceof Constructor) {
    // Called as a constructor
    // ...
  } else {
    // Called as a function
    return new Constructor();
  }
}

这也允许您为每种情况实现不同的行为,例如String,例如

typeof String(); // "string"
typeof new String(); // "object"`.

如Oriol所述,prototypeprototype.constructor属性是在创建函数时指定的。不过,他的解决方案仍然包含new关键字,您似乎希望避免使用该关键字。

箭头函数不分配构造函数属性

但是,箭头函数没有自动创建prototypeprototype.constructor属性,并且不能使用new关键字实例化它们。

如果您不特别需要使用箭头函数,我建议您使用经典的命名函数表达式。由于chrome似乎可以推断匿名函数的名称,因此您可能不需要该名称。

重写Tab.prototype也重写构造函数属性

不保留.constructor属性的另一个原因是,在为其分配对象时,您将覆盖整个Tab.prototype。相反,您可以以一种单一的方式分配属性:

const Tab = function Tab({id, windowId}) { return Object.assign(Object.create(Tab.prototype), {id, windowId}) };
Tab.prototype.toString = function() {
    return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
  }
};

或者您可以使用Object.assign将额外的属性添加到Tab.prototype:

const Tab = function Tab({id, windowId}) { return Object.assign(Object.create(Tab.prototype), {id, windowId}) };
Object.assign(Tab.prototype, {
    toString() {
       return `${this.constructor.name} ${JSON.stringify(Object.values(this))}`
    }
});

如果你正在编写一个可公开访问的函数,并且想禁止其他人在你的函数上使用new运算符,那么你可以使用new.target来阻止这种情况的发生:

const Tab = function Tab({id, windowId}) {
    if (new.target) {
        throw new Error(`${ this.constructor.name } is not a constructor`);
    }
    return Object.assign(Object.create(Tab.prototype), {id, windowId})
};
// ...