将javascript类实例转换为普通对象保留方法

时间:2016-01-09 22:12:02

标签: javascript typescript ecmascript-6 prototypal-inheritance

我想将实例类转换为普通对象,而不会丢失方法和/或继承的属性。例如:

class Human {
    height: number;
    weight: number;
    constructor() {
        this.height = 180;
        this.weight = 180;
    }
    getWeight() { return this.weight; }
    // I want this function to convert the child instance
    // accordingly
    toJSON() {
        // ???
        return {};
    }
}
class Person extends Human {
    public name: string;
    constructor() {
        super();
        this.name = 'Doe';
    }
    public getName() {
        return this.name;
    }
}
class PersonWorker extends Person {
    constructor() {
        super();
    }
    public report() {
        console.log('I am Working');
    }
    public test() {
        console.log('something');
    }
}
let p = new PersonWorker;
let jsoned = p.toJSON();

jsoned应如下所示:

{
    // from Human class
    height: 180,
    weight: 180,
    // when called should return this object's value of weight property
    getWeight: function() {return this.weight},

    // from Person class
    name: 'Doe'
    getName(): function() {return this.name},

    // and from PersonWorker class
    report: function() { console.log('I am Working'); },

    test: function() { console.log('something'); }
}

这有可能实现,如果可以,怎么做?

如果您想知道,我需要这个,因为我使用的框架不幸地接受了输入 只有一个json对象,而我正在尝试使用typescript和类继承。

此外,我正在进行上述转换,因此性能不是一个需要考虑的问题。

更新

如果编译器的目标选项设置为es6,则通过迭代对象属性组成的解决方案将不起作用。在es5上,通过迭代对象属性(使用Object.keys(instance))的现有实现将起作用。

到目前为止,我有这个实现:

toJSON(proto?: any) {
    // ???

    let jsoned: any = {};
    let toConvert = <any>proto || this;

    Object.getOwnPropertyNames(toConvert).forEach((prop) => {
        const val = toConvert[prop];
        // don't include those
        if (prop === 'toJSON' || prop === 'constructor') {
            return;
        }
        if (typeof val === 'function') {
            jsoned[prop] = val.bind(this);
            return;
        }
        jsoned[prop] = val;
        const proto = Object.getPrototypeOf(toConvert);
        if (proto !== null) {
            Object.keys(this.toJSON(proto)).forEach(key => {
                if (!!jsoned[key] || key === 'constructor' || key === 'toJSON') return;
                if (typeof proto[key] === 'function') {
                    jsoned[key] = proto[key].bind(this);
                    return;
                }
                jsoned[key] = proto[key];
            });
        }
    });
    return jsoned;
}

但这仍然无效。结果对象仅包括所有类中的所有属性,但仅包括PersonWorker中的方法。 我在这里缺少什么?

7 个答案:

答案 0 :(得分:6)

已经有很多答案,但这是最简单的,使用 ... 运算符并解构对象:

const {...object} = classInstance

答案 1 :(得分:3)

好的,所以我的OP中的实现是错误的,错误只是愚蠢。

使用es6时的正确实施是:

toJSON(proto) {
    let jsoned = {};
    let toConvert = proto || this;
    Object.getOwnPropertyNames(toConvert).forEach((prop) => {
        const val = toConvert[prop];
        // don't include those
        if (prop === 'toJSON' || prop === 'constructor') {
            return;
        }
        if (typeof val === 'function') {
            jsoned[prop] = val.bind(jsoned);
            return;
        }
        jsoned[prop] = val;
    });

    const inherited = Object.getPrototypeOf(toConvert);
    if (inherited !== null) {
        Object.keys(this.toJSON(inherited)).forEach(key => {
            if (!!jsoned[key] || key === 'constructor' || key === 'toJSON')
                return;
            if (typeof inherited[key] === 'function') {
                jsoned[key] = inherited[key].bind(jsoned);
                return;
            }
            jsoned[key] = inherited[key];
        });
    }
    return jsoned;
}

答案 2 :(得分:3)

这就是对我有用的

const classToObject = theClass => {
  const originalClass = theClass || {}
  const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(originalClass))
  return keys.reduce((classAsObj, key) => {
    classAsObj[key] = originalClass[key]
    return classAsObj
  }, {})
}

enter image description here

答案 3 :(得分:1)

我非常喜欢Alex Cory的解决方案,但这就是我想到的。期望将其作为功能分配给类,并在this上具有相应的绑定。

const toObject = function() {
  const original = this || {};
  const keys = Object.keys(this);
  return keys.reduce((classAsObj, key) => {
    if (typeof original[key] === 'object' && original[key].hasOwnProperty('toObject') )
      classAsObj[key] = original[key].toObject();
    else if (typeof original[key] === 'object' && original[key].hasOwnProperty('length')) {
      classAsObj[key] = [];
      for (var i = 0; i < original[key].length; i++) {
        if (typeof original[key][i] === 'object' && original[key][i].hasOwnProperty('toObject')) {
          classAsObj[key].push(original[key][i].toObject());
        } else {
          classAsObj[key].push(original[key][i]);
        }
      }
    }
    else if (typeof original[key] === 'function') { } //do nothing
    else
      classAsObj[key] = original[key];
    return classAsObj;
  }, {})
}

然后,如果您使用的是TypeScript,则可以将此接口放在应转换为对象的任何类上:

export interface ToObject {
  toObject: Function;
}

然后在您的课程中,不要忘记绑定this

class TestClass implements ToObject {
   toObject = toObject.bind(this);
}

答案 4 :(得分:0)

这是toJSON()方法的实现。我们正在复制这些属性&amp;从当前实例到新对象的方法,并排除不需要的方法,即toJSON和constructor。

toJSON() {
    var jsonedObject = {};
    for (var x in this) {

        if (x === "toJSON" || x === "constructor") {
            continue;
        }
        jsonedObject[x] = this[x];
    }
    return jsonedObject;
}

我已经测试了Chrome中toJSON()返回的对象,我发现该对象的行为与您期望的相同。

答案 5 :(得分:0)

使用Lodash

此方法不是递归的。

  toPlainObject() {
    return _.pickBy(this, item => {
      return (
        !item ||
        _.isString(item) ||
        _.isArray(item) ||
        _.isNumber(item) ||
        _.isPlainObject(item)
      );
    });
  }

答案 6 :(得分:-1)

这个方案会丢失方法,但是将类实例转换为对象是一个非常简单的方案。

obj = JSON.parse(JSON.stringify(classInstance))