【问题标题】:convert javascript plain object into model class instance将javascript普通对象转换为模型类实例
【发布时间】:2016-02-20 02:56:11
【问题描述】:

我需要实现类似 ODM 的小型功能。我从数据库中获取普通的 javascript 对象,我需要将其转换为我的模型类实例。假设模型如下所示:

    class Model{
       constructor(){
           this.a = '777';
           ---- whole bunch of other things ---
       }
       print(){
           console.log(this.a);
       }
   }

所以我需要将var a = {b:999, c:666} 转换为模型实例并能够在之后调用a.print(),并且当a.print() 执行时777 应该放在控制台中。该怎么做?

【问题讨论】:

  • {b:999, c:666} 怎么会变成Model 实例?您的 Models 只有 a 属性,而不是 bc 属性。也许这就是人们不理解您的问题的原因。
  • @Bergi 它可能是对象中的几十个字段,我认为它们都不应该在构造函数中列出。
  • @silent_coder:当然你所有的字段都应该在构造函数中列出来?如果没有创建,实例将不会有这些字段。
  • @Bergi 这是 javascript 伙伴。你可以用任何方法输入this.b = xxx,它是完全有效的。
  • 在我看来,这就像 Casting plain objects to function instances (“classes”) in javascript 的副本(在 ES6 中没有什么不同)。请告诉我这是否有帮助。

标签: javascript ecmascript-6 odm


【解决方案1】:

这个怎么样?

var a = Object.create(Model.prototype, {
    b: {
        enumerable: true, // makes it visible for Object.keys()
        writable: true, // makes the property writable
        value: 999
    }, c: {
        value: 666
    }
});

你基本上是从它的原型创建一个新的 Model 实例并将你的新属性分配给它。你应该也可以拨打print

【讨论】:

【解决方案2】:

如果我理解正确,您可以导出一个工厂函数并使用Object.assign 扩展您的基础Model

// Export the factory function for creating Model instances
export default const createModel = function createModel(a) {
  const model = new Model();
  return Object.assign(model, a);
};
// Define your base class
class Model {
  constructor() {
    this.a = 777;
  }
  print() {
    console.log(this.a, this.b, this.c)
  }
}

然后这样称呼它:

const myModel = createModel({ b: 999, c: 666 });
myModel.print();

Babel REPL Example

或者,当然,您可以放弃工厂并将a 作为参数(或其余参数)传递给构造函数,但这取决于您喜欢的编码风格。

【讨论】:

  • 我会推荐class Model { static create(a) { … } … },也许还有一个更具描述性的名字,比如createFromfromJSON
  • 如果a.a 最初包含不同的值会怎样。比方说'-500'?我认为一些数据会丢失。或者在普通对象中,或者在构造函数中设置。如果随时间的模型将使用属性扩展并且只读 getter 与普通对象中的字段同名,这种情况将如何工作?
  • @Ph0en1x 问题中没有指定任何内容。无论哪种方式,将Object.assign 中的 source 对象转换为排除键都是微不足道的。
  • @Ph0en1x:试试看?最后一个值将覆盖前一个值。稍后(在构造之后)扩展实例是一种反模式。只读 getter 当然会导致问题,您必须调整 createModel 方法以明确使用它们。
【解决方案3】:

就像 G_hi3 的回答,但它“自动”创建属性对象

function Model() {
  this.a = '777';
}

Model.prototype.print = function(){
    console.log(this.a);
}

   // Customize this if you don't want the default settings on the properties object.
function makePropertiesObj(obj) {
    return Object.keys(obj).reduce(function(propertiesObj, currentKey){
        propertiesObj[currentKey] = {value: obj[currentKey]};
        return propertiesObj;
    }, {}); // The object passed in is the propertiesObj in the callback
}

var data = {a: '888'};

var modelInstance = Object.create(Model.prototype, makePropertiesObj(data));
// If you have some non trivial initialization, you would need to call the constructor. 
Model.call(modelInstance);
modelInstance.print(); // 888

【讨论】:

  • 不过,这仍然缺少对构造函数的调用。
  • @Bergi 我故意没有调用构造函数,因为它会覆盖this.a = '777'; 中传递的数据。
  • 你可以(应该)在传入数据之前调用它,我的意思是。如果您不调用它,您可能缺少实例的初始化。
【解决方案4】:

我建议重写你的类,将其所有属性存储在单个 JS 对象 this.props 中,并在其构造函数中接受该对象:

class Model {
  constructor (props = this.initProps()) {
    this.props = props
    // other stuff
  }
  initProps () {
    return {a: '777'}
  }
  print () {
    console.log(this.props.a)
  }
}

然后你就可以将this.props作为一个普通的JS对象存储在你的数据库中,然后用它轻松地重新创建相应的类实例:

new Model(propsFromDatabase)

不过,如果您不想将所有属性移动到 this.props,您可以使用 Object.assign 来保持对象的简单:

class Model {
  constructor (props = this.initProps()) {
    Object.assign(this, props)
    // other stuff
  }
  initProps () {
    return {a: '777'}
  }
  print () {
    console.log(this.a)
  }
}

但我建议使用前一种方法,因为它可以保护您免受名称冲突。

【讨论】:

  • 这并不单独默认每个属性,而是仅默认整个props 对象。
  • @Bergi 是的,这是您对 ODM 的期望——要么从 DB 中读取所有内容,要么从头开始创建。
  • 另外,新的Model({b:999, c:666}).print() // a === undefined
  • @RGraham 是的,因为它在this.props.a 中。但无论如何,好点。添加了替代方法。
  • 这种方法有严重的缺点。如果您以后需要使用您的属性,例如(new Model(a)).b,您将需要为任何属性实现自定义 getter。这是大量的工作。
【解决方案5】:

有一个简单的方法。只需将对象分配给实例(this)

class Model
{
  constructor(obj){
    Object.assign(this, obj)
  }
  print(){
    console.log(this.a);
  }
}

let obj = {a: 'a', b: 'b', c: 'c'}
    
let m = new Model(obj)
console.log(m)
m.print()  // 'a'

【讨论】:

    【解决方案6】:

    如果您需要更一致的类型转换,您还可以创建自己的typecast 函数,如泛型函数

    function typecast(Class, obj) {
      let t = new Class()
      return Object.assign(t,obj)
    }
    
    // arbitrary class
    class Person {
     constructor(name,age) {
       this.name = name
       this.age = age
     }
     print() {
       console.log(this.name,this.age)
     }
    }
    

    调用它将任何对象类型转换为任何类实例,例如

    let person = typecast(Person,{name:'Something',age:20})
    person.print() // Something 20
    

    【讨论】:

      【解决方案7】:

      你可以有一个静态的 Model.fromModel.parse 方法,它返回一个具有这些属性的新模型:

      class Model {
        static defaults = { a: 777, b: 888, c: 999, d: 111, e: 222 };
        constructor() {
          const { defaults } = Model;
          for (const key in defaults) this[key] = defaults[key];
        }
        print() {
          console.log(this.a);
        }
        static from(data) {
          const { defaults } = Model;
          return Object.assign(
            new Model(), 
            Object.keys(data)
              .filter((key) => defaults[key])
              .reduce((prev, curr) => ({
                ...prev, [curr]: data[curr] 
              }), defaults)
          );
        }
      }
      
      const data = {
        a: "a", b: "b", c: "c", ajkls: "this wont be included"
      };
      const myModel = Model.from(data);
      console.log("myModel =", myModel);
      console.log("myModel instanceof Model:", myModel instanceof Model);
      console.log("myModel.print():")
      myModel.print();

      【讨论】:

        猜你喜欢
        • 2012-08-02
        • 2016-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多