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

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

我目前正在使用 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 operator does not create a constructor property. It just calls the [[Construct]] 内部方法。

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

prototypeconstructor 属性只创建一次,当您 create the function.

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

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})
};

// ...