【问题标题】:How to merge JavaScript class instance with object?如何将 JavaScript 类实例与对象合并?
【发布时间】:2018-12-18 14:31:39
【问题描述】:

我想将对象的属性/值与类实例合并。 (我不确定 JS 中的正确术语是什么,但示例应该澄清)

我的尝试是使用扩展语法。见下文。

我有一个File-instance:

const files = listOfFilesFromDragNdrop();
let file = files[0];

console.log(file)

输出类似:

File(2398)
lastModified: 1530519711960
lastModifiedDate: Mon Jul 02 2018 10:21:51 GMT+0200
name: "my_file.txt"
preview: "blob:http://localhost:8080/6157f5d5-925a-4e5d-a466-24576ba1bf7c"
size: 2398
type: "text/plain"
webkitRelativePath: ""

添加后,我使用FileReader.readAsText() 获取内容,并将其包装在如下对象中:

contentObject = getFileContentFromFile()
console.log(contentObject)

会输出如下内容:

{ 
    preview: "blob:http://localhost:8080/6157f5d5-925a-4e5d-a466-24576ba1bf7c",
    content: "Lorem ipsum some text here." 
}

我想最终得到一个合并的对象,例如:

{ 
    // "preview" is the key used to map files and content
    preview: "blob:http://localhost:8080/6157f5d5-925a-4e5d-a466-24576ba1bf7c",

    // "text" is the new field with the content from contentObject
    text: "Lorem ipsum some text here." 

    // The other fields are from the File instance
    name: "my_file.txt",
    size: 2398,
    type: "text/plain",
    lastModified: 1530519711960,
    // ...        
}

我首先尝试的是:

const mergedObject = {
    ...file,
    text: contentObject.content
}

同样(知道text 键会变成content)我试过了

const mergedObject = {
    ...file,
    ...contentObject
}

但是,然后我只得到contentObject 字段,即mergedObject 类似于contentObject。有趣的是,如果我这样做了

const mergedObject = {
    ...file
}

mergedObject 是一个 File 实例。我假设扩展运算符对类实例的工作方式与对对象的工作方式不同?如何实现合并的对象

更多不必要的信息

  • FileReader 在 redux 中间件中实现,并在完成读取后以 { preview: '1234..ef', text: 'Lorem ipsum'} 对象作为负载分派一个新操作。
  • 我正在使用preview-字段将内容映射到文件,并希望在“文件”-reducer 中返回合并的对象,例如:return files.map(file => file.preview !== payload.preview? file: {...file, text: payload.content}

【问题讨论】:

  • 您说您正在使用FileReader.readAsText“获取内容,例如在对象中”。这不是要返回一个字符串,而不是一个对象吗?也许我误会了一步。
  • 你是对的,但它是异步的,所以我将它包装在一个对象中来处理回调。
  • 我的例子有点简化,因为它是 Redux reducer 和 Middleware 的一部分

标签: javascript spread-syntax


【解决方案1】:

要合并一个类实例和一个对象,在 ES6 中,您可以使用 Object.assign() 来合并对象中的所有属性,并保持类实例的原始原型。扩展运算符只合并所有属性,但不合并原型。

在你的情况下,尝试:

const mergedObject = Object.assign(file, contentObject)

请记住,在这种情况下,您的原始文件对象将被更改。

【讨论】:

    【解决方案2】:

    你可能只需要做这样的事情......

    const mergedObject = {
      lastModified: file.lastModified,
      lastModifiedDate: file.lastModifiedDate,
      name: file.name,
      size: file.size,
      type: file.type,
      webkitRelativePath: file.webkitRelativePath,
      text: contentObject.content,
      preview: contentObject.preview,
    }

    您可以编写一个实用函数来从文件实例中提取伪属性:

    // Error is a like File with a pseudo property named message
    let error = new Error('my error message')
    error.status = 404;
    
    const pick = (objectLike, properties) =>
        properties.reduce(
            (acc, key) => {
                acc[key] = objectLike[key];
                return acc;
            },
            {}
        );
    
    const contentObject = {
        content: 'content text',
        preview: 'http://url.io',
    };
    
    const mergedObject = {
      ...pick(error, Object.getOwnPropertyNames(error)),
      ...contentObject,
    }
    console.log(JSON.stringify(mergedObject));

    Lodash 有一个你可以用来做这个的选择函数。

    【讨论】:

    • 我希望我不必这样做,但似乎这是唯一的方法。谢谢。
    • @ThomasFauskanger,我用一种可能的实用方法更新了答案,你可以编写它以更接近我认为你想要的。
    • 感谢添加的建议解决方案。 (在我的解决方案中,我实际上为解决这个问题所做的是将 File 实例与文本一起嵌入到像 const allInformation = {instance: file, text: content)} 这样的包装对象中。)
    • 请注意,此技术不会为对象类可能扩展的父类中定义的属性创建引用。
    【解决方案3】:

    像循环这样的传播语法会迭代可枚举的属性。正如您所看到的,下面的代码显示 File 对象的 name 属性不可枚举。因此,获得这些属性的唯一方法是一个一个。

    document.querySelector('input').addEventListener('change', e => {
      const file = e.target.files[0];
      console.log(file.propertyIsEnumerable('name'));
    });
    <input type="file">

    【讨论】:

    • 感谢您解释为什么传播语法不适用于 File 实例。
    【解决方案4】:

    您可以沿着原型链向上走并创建一个包装类实例的 POJO。一旦你有了合并是微不足道的。

    // just a sample object hierarchy
    
    class Plant {
      constructor() {
        this._thorns = false;
      }
      hasThorns() {
        return this._thorns;
      }
    }
    
    class Fruit extends Plant {
      constructor(flavour) {
        super();
        this._flavour = flavour;
      }
      flavour() {
        return this._flavour;
      }
    }
    
    // this is the mechanism
    
    function findProtoNames(i) {
      let names = [];
      let c = i.constructor;
      do {
        const n = Object.getOwnPropertyNames(c.prototype);
        names = names.concat(n.filter(s => s !== "constructor"));
        c = c.__proto__;
      } while (c.prototype);
    
      return names;
    }
    
    function wrapProto(i) {
      const names = findProtoNames(i);
      const o = {};
      for (const name of names) {
        o[name] = function() {
          return i[name].apply(i, arguments);
        }
      }
    
      return o;
    }
    
    function assignProperties(a, b) {
      
      for (const propName of Object.keys(b)) {
        if (a.hasOwnProperty(propName)) {
          const msg = `Error merging ${a} and ${b}. Both have a property named ${propName}.`;
          throw new Error(msg);
        }
    
        Object.defineProperty(a, propName, {
          get: function() {
            return b[propName];
          },
          set: function(value) {
            b[propName] = value;
          }
        });
      }
    
      return a;
    }
    
    function merge(a, b) {
      if (b.constructor.name === "Object") {
        return Object.assign(a, b);
      } else {
        const wrapper = wrapProto(b);
        a = assignProperties(a, b);
        return assignProperties(a, wrapper);
      }
    }
    
    // testing it out
    
    const obj = {
      a: 1,
      b: 2
    };
    const f = new Fruit('chicken');
    const r = merge(obj, f);
    console.log(r);
    console.log('thorns:', r.hasThorns());
    console.log('flavour:', r.flavour());

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-16
      • 2020-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多