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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34699529/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:12:31  来源:igfitidea点击:

Convert javascript class instance to plain object preserving methods

javascripttypescriptecmascript-6prototypal-inheritance

提问by eAbi

I want to convert an instance class to plain object, without losing methods and/or inherited properties. So for example:

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

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();

jsonedshould look like this:

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'); }
}

Is this possible to achieve, and if so, how?

这是否可以实现,如果可以,如何实现?

In case you're wondering, I need this because I am using a framework that, unfortunately, accepts as input only an object, whereas I am trying to use TypeScript and class inheritance.

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

Also, I am doing the above conversion once so performance isn't an issue to consider.

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

The solutions consisting of iterating through object properties will not work if the compiler's target option is set to es6. On es5, the existing implementations by iterating through object properties (using Object.keys(instance)) will work.

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

So far, I have this implementation:

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

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;
}

But this is still not working. The resulted object includes only all the properties from all classes but only methods from PersonWorker. What am I missing here?

但这仍然不起作用。结果对象仅包含来自所有类的所有属性,但仅包含来自PersonWorker. 我在这里错过了什么?

采纳答案by eAbi

Ok, so the implementation in my OP was wrong, and the mistake was simply stupid.

好的,所以我的 OP 中的实现是错误的,这个错误简直是愚蠢的。

The correct implementation when using es6is:

使用时的正确实现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;
}

回答by Alex Cory

This is what's working for me

这对我有用

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

在此处输入图片说明

回答by RobotOptimist

I'm riffing on Alex Cory's solution a lot, but this is what I came up with. It expects to be assigned to a class as a Function with a corresponding bind on this.

我经常重复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;
  }, {})
}

then if you're using TypeScript you can put this interface on any class that should be converted to an object:

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

export interface ToObject {
  toObject: Function;
}

and then in your classes, don't forget to bind this

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

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

回答by alvaro.canepa

using Lodash

使用 Lodash

This method isn't recursive.

这种方法不是递归的。

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

回答by Seshu Kumar Alluvada

Here is the implementation for the toJSON() method. We are copying over the properties & methods from the current instance to a new object and excluding the unwanted methods i.e. toJSON and constructor.

这是 toJSON() 方法的实现。我们将属性和方法从当前实例复制到一个新对象,并排除不需要的方法,即 toJSON 和构造函数。

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

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

I have tested the object returned by toJSON() in Chrome and I see the object behaving the same way as you are expecting.

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