【问题标题】:JavaScript: Should a function be able to create instances of itself with Object.create()JavaScript:函数是否应该能够使用 Object.create() 创建自身的实例
【发布时间】:2015-12-06 11:10:48
【问题描述】:

我的用例如下:我想创建一个生产各种数据传输对象 (DTO) 的工厂。它们必须易于序列化,并且必须有一些额外的方法。

我当前的实现如下所示 (simplified):

window.Dto = function(type, properties)
{
    var
        self = this,
        values = {},
        object = Object.create(self);

    properties.forEach(function(prop){
        Object.defineProperty(object, prop, {
            get: function() { return values[prop]; },
            set: function(value) { values[prop] = value; },
            enumerable: true
        });
    });

    this.getType = function()
    {
        return type;
    };

    this.doSomeMagic = function()
    {
        // ...
    };

    return object;
};

// creating a DTO of the Transport.Motorized.Car class
var carObject = new Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);

(注意:我不想为这些对象中的每一个创建一个显式类,因为它们有很多,并且它们是从服务器端导出的。此外,您在上面看到的 properties 参数是实际上是带有验证约束等的元数据映射)

我在一个创建了 50,000 个此类对象的循环中进行了快速性能检查。 performance.now() 告诉我它花了超过 1 秒的时间——看起来不错,但不是太令人印象深刻。

我的问题主要是:工厂可以从自己的原型创建一个实例(如果我正确理解该代码的作用)并返回它吗?它会有什么副作用?有没有更好的办法?

【问题讨论】:

  • 一般来说,我不会将new 运算符用于工厂函数。相反,我会在函数内部创建一个对象用作原型:您可以使用this,而不是使用var factoryProto = {}; factoryProto.getType = ...; object = Object.create(factoryProto);
  • @nils:新创建的对象需要访问Dto的“私有”成员,因此我无法从外部创建实例。有元数据,例如需要访问元数据的验证函数。在序列化时,我不想看到附加的属性/方法。

标签: javascript prototype ecmascript-5


【解决方案1】:

据我了解工厂函数,它们的全部意义在于不需要创建函数本身的新实例。相反,它只是返回一个新创建的对象。

因此,我不会使用新创建的实例(通过new 运算符)的实例属性(通过this),而是创建一个对象(我们称之为factoryProto)并分配所有“实例”代替该对象的方法。

然后,您可以将factoryProto 用作您的新[[Prototype]]object

window.Dto = function(type, properties) {
    var factoryProto = {
          getType: function() {
            return type;
          },
          doSomeMagic: function() {
              // ...
          }
        },
        values = {},
        object = Object.create(factoryProto);

    properties.forEach(function(prop) {
        Object.defineProperty(object, prop, {
            get: function() { return values[prop]; },
            set: function(value) { values[prop] = value; },
            enumerable: true
        });
    });

    return object;
};

// creating a DTO of the Transport.Motorized.Car class
var carObject = Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);

如果您想充分利用原型链,您可以在工厂函数之外定义factoryProto。要跟踪type,您可以将其添加为不可枚举的object 属性:

window.Dto = (function() {
    var factoryProto = {
        getType: function() {
          return this.type;
        },
        doSomeMagic: function() {
            // ...
        }
    };

    return function(type, properties) {
        var values = {},
            object = Object.create(factoryProto);

        properties.forEach(function(prop) {
            Object.defineProperty(object, prop, {
                get: function() { return values[prop]; },
                set: function(value) { values[prop] = value; },
                enumerable: true
            });
        });

        Object.defineProperty(object, 'type', {
          value: type,
          enumerable: false
        });

        return object;
    };
})();

// creating a DTO of the Transport.Motorized.Car class
var carObject = Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);

【讨论】:

  • 是的,这样更好。天哪,在我开始摆弄Object.create 之前,我昨天晚上也有类似的事情。我只需要把它放在一起。 ;)
  • 顺便说一句,如果你使用 getter 和 setter,JSON.stringify 是行不通的(据我所知)。
  • 它在 FF42 中确实有效。但是感谢您的提及,在其他/较旧的浏览器中进行测试时,我会牢记这一点。
  • 只是一个指针,因为您计划创建很多实例:如果您在工厂函数中定义 factoryProto,您将不会获得原型系统的性能优势(因为 factoryProto 是在每次调用window.Dto 时重新定义)。不过,您可以使用 IIFE 和不可枚举的属性来解决这个问题。
  • 我刚刚用可能的解决方案更新了答案。不可枚举的属性不会被 JSON.stringify 解析:stackoverflow.com/questions/15733878/…
猜你喜欢
  • 2011-02-04
  • 1970-01-01
  • 1970-01-01
  • 2018-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
相关资源
最近更新 更多