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

时间:2016-05-01 16:32:34

标签: javascript ecmascript-6 ecmascript-5

我目前正在使用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}};例如,[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))}`
  }
})

2 个答案:

答案 0 :(得分:4)

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

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

当您create the function时,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"`.

答案 1 :(得分:1)

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

// ...