如何将 JavaScript 类实例与对象合并?
Posted
技术标签:
【中文标题】如何将 JavaScript 类实例与对象合并?【英文标题】:How to merge JavaScript class instance with object? 【发布时间】: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 的一部分
【参考方案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());
【讨论】:
以上是关于如何将 JavaScript 类实例与对象合并?的主要内容,如果未能解决你的问题,请参考以下文章