Skip to main content
 首页 » 编程设计

javascript之对象字面量中的工厂模式以实现可迭代性和反射

2025年01月19日8grandyang

我希望能够从工厂构造实例并让它们看起来像 new Instance() 创建的实例-- 意味着构造函数和原型(prototype)与 new 创建的实例没有区别.

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

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

请注意,“类名”“实例”不会传递给通用创建者。

另一个要求是这个 Instance 的成员驻留在对象字面量中,如果未提供构造函数,则使用默认值(无操作)。

驻留在文字中的成员用于迭代和反射,这就是为什么任何在空白处执行操作的方法都行不通的原因。

/* 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 

当您声明一个函数时,它会自动获得一个原型(prototype),其中构造函数字段设置为该函数。您必须使用 rv.constructor.prototype.constructor=that._ctor; 设置原型(prototype)的构造函数字段,因为您用模板替换了原型(prototype)。您可以在 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);