将javascript普通对象转换为模型类实例

Posted

技术标签:

【中文标题】将javascript普通对象转换为模型类实例【英文标题】:convert javascript plain object into model class instance 【发布时间】: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 中没有什么不同)。请告诉我这是否有帮助。 【参考方案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

【讨论】:

我需要编写自动化代码来将 pojso 转换为模型实例。我无法手动填写属性。 是的,属性也可以用enumerablewritableconfigurablegetset这样修改:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…【参考方案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();

【讨论】:

以上是关于将javascript普通对象转换为模型类实例的主要内容,如果未能解决你的问题,请参考以下文章

javascript 面向对象设计之 Function 普通类

django的模型类管理器-----------数据库操作的封装

如何将 JsonObject 转换为类模型 kotlin android

将继承类的对象转换为继承类型的实例

如何从原始对象创建一个Eloquent模型实例?

将unix时间戳转换为javascript日期对象[重复]