对象文本内部的工厂模式,以实现可迭代性和反射性

Factory pattern inside object literal to accomplish iterability and reflection

本文关键字:实现 迭代 反射性 内部 文本 工厂 模式 对象      更新时间:2023-09-26

我希望能够从工厂构建实例,并使它们看起来像new Instance()创建的实例——这意味着构造函数和原型与new创建的实例无法区分。

工厂将驻留在对象文字中,因此它的调用看起来像

var instance=App.factory.Instance.create(arg1,...);

请注意,"classname"Instance"不会传递给泛型创建者。

另一个要求是这个Instance的成员驻留在对象文字中,如果没有提供构造函数,则使用默认值(没有op)。

驻留在文本中的成员是为了迭代和反思,这就是为什么任何在空白中解决问题的方法都不起作用。

/* jshint laxcomma: true */
window.App={
      _name:'App'// convention
      ,factory:{
        _name:'factory'
        // begin specifying classes
        ,Instance:{ // this is a mini-factory of type 'Instance',
                    // a 'create' method is dynamically injected when 'factory' is initialized
          _name:'Instance'
          ,ctor:function Instance(a,b){ // OPTIONAL specified constructor, want to ditch fn name here if possible
          }
          ,template:{ // Instance spec container. Is like 'Instance.prototype'
            //_name:'template' // convention, but will not be a member of 'instance'
            valueMember:'something' // this will exist on finished 'prototype'
            ,funcMember:function(){  // this will exist on finished 'prototype'
            }
          }
          ,_static:{ // these will be placed on 'Instance' (constructor)
            _name:'_static' // convention, but not placed
            ,valueMember:'something' // this will exist on 'Instance' (constructor)
            ,funcMember:function(){  // this will exist on 'Instance' (constructor)
            }
          }
          //,create:function(){} is injected during init of factory
        }// - Instance
        // more classes like Instance here...
      }
    };
App.factory.Instance.create=function(){ // this must be generic/agnostic to 'Instance'
      var that=this; // this is 'Instance:'
      function init(){
        if (that.ctor===undefined) { // user did not provide constructor
          //that.ctor=new Function(''); // BAD WAY
          // make a generic constructor with correct name
          that.ctor=eval('(function '+that._name+'(){})');
        }
        // preserve constructor for reuse
        that._ctor=that.ctor;        
        delete that.ctor;
        var i;
        if (typeof that._static==='object') { // put statics on constructor
          for (i in that._static) {
            if (i!=='_name') {                // ignore convention
              that._ctor[i]=that._static[i];
            }
          }
        }
        // doing it whole here, should be a cleaned-of-convention duplicate object
        that._ctor.prototype=that.template;
        that._ctor.name=that._name;
        // this line confuses me, something should be done, do I need it?
        //that._ctor.prototype.constructor=that._ctor; // produces 'constructor' in instance        
      }
      // look in cache
      if (that._ctor===undefined) {
        init();
      }
      // NOW THE HARD PART - HOW TO INVOKE
      var rv;
      var fn=that._ctor;
      //rv=construct(fn,arguments);
      //rv= new fn();
      rv=new that._ctor();
      // HERE
      // fn.prototype.constructor=fn;
      // My problem at this point is getting instance.constructor to
      // not be 'Object' but if I include the line above
      // it shows 'constructor' as iterable
      // however THIS LINE WORKS, but why? BUT Why do I have to do it here, after construction?
      rv.constructor.prototype.constructor=that._ctor;
      // NO that._ctor.prototype.constructor=that._ctor; puts 'constructor' on instance
      return rv;
      }; //-create
function Classic(a,b){
}
Classic.prototype.member="member";
var classic=new Classic();
var instance=App.factory.Instance.create();
console.log('classic',classic);
console.log('instance',instance);
console.log('classic.constructor',classic.constructor);
console.log('instance.constructor',instance.constructor);
console.log('instance.constructor.name',instance.constructor.name);
console.log('classic.constructor.name',classic.constructor.name);

我已经完成了一半,但我感到困惑,并希望在基本方法上有所改进。此外,我是否遗漏了任何可以使instance与经典构造对象区分开来的接线?

扩展这一想法,似乎应该有一个库来接受这种模板化,并生成通过Object.defineProperty构建类的工厂,从而提供更多的控制权,即_readonly等。一种语言中的语言。

JSBin

,ctor:function Instance(a,b){ // OPTIONAL specified constructor, want to ditch fn name here if possible

如果稍后创建一个name等于_name的函数,只调用ctor(如ctor.call(this); ),则可以在此处删除类的名称

App.factory.Instance.create=function(){ // this must be generic/agnostic to 'Instance'

看起来,为了不可知Instance,您添加了_name约定。您还可以创建一个函数来接受App对象,并递归地添加_name字段。另一种可能性是将factory而不是Instance传递给要添加create方法的函数。

that._ctor.name=that._name;

此代码不执行任何操作,因为函数的名称是只读属性。也不需要它,因为代码eval('(function '+that._name+'(){})')已经创建了具有所需名称的函数。

fn.prototype.constructor=fn

当您声明一个函数时,它会自动获得一个原型,其中构造函数字段设置为该函数。您必须用rv.constructor.prototype.constructor=that._ctor;设置原型的构造函数字段,因为您用模板替换了原型。您可以在rv=new that._ctor();之前或之后执行此操作。

classic instanceof Classic
instance instanceof App.factory.Instance

这将使工厂创建的对象与正常创建的对象区分开来。为了使instanceof运算符工作,App.factory.Instance需要是构造函数。

这是我根据您的要求编写的代码。

App = {
    factory: {
        Instance: {
            template: {
                c: 3,
                d: 4
            },
            _static: {
                e: 5,
                f: 6
            },
            constructor: function (a, b) {
                this.a = a;
                this.b = b;
            }
        }
    }
};
function init(factory) {
    for (var name in factory) {
        var constructor = eval('(function ' + name + '(){})');
        constructor.prototype = factory[name].template;
        Object.defineProperty(constructor.prototype, 'constructor', {
            value: constructor,
            enumerable: false
        });
        for (var property in factory[name]._static) {
            constructor[property] = factory[name]._static[property];
        }
        var previous = factory[name].constructor;
        var create = function () {
            var instance = new constructor();
            if (previous) {
                previous.apply(instance, arguments);
            }
            return instance;
        }
        factory[name] = constructor;
        factory[name].create = create;
    }
}
init(App.factory);
function Classic(a, b) {
    this.a = a;
    this.b = b;
};
Classic.prototype.c = 3;
Classic.prototype.d = 4;
Classic.e = 5;
Classic.f = 6;
var classic = new Classic(1, 2);
var instance = App.factory.Instance.create(1, 2);
console.log('classic', classic);
console.log('instance', instance);
console.log('classic.constructor', classic.constructor);
console.log('instance.constructor', instance.constructor);
console.log('instance.constructor.name', instance.constructor.name);
console.log('classic.constructor.name', classic.constructor.name);
console.log('classic instanceof Classic', classic instanceof Classic);
console.log('instance instanceof App.factory.Instance', instance instanceof App.factory.Instance);
console.log('classic.a', classic.a);
console.log('classic.b', classic.b);
console.log('classic.c', classic.c);
console.log('classic.d', classic.d);
console.log('Classic.e', Classic.e);
console.log('Classic.f', Classic.f);
console.log('instance.a', instance.a);
console.log('instance.b', instance.b);
console.log('instance.c', instance.c);
console.log('instance.d', instance.d);
console.log('App.factory.Instance.e', App.factory.Instance.e);
console.log('App.factory.Instance.f', App.factory.Instance.f);