如何将 JavaScript class 实例与对象合并?
How to merge JavaScript class instance with object?
我想将对象的 properties/values 与 class 实例合并。 (我不确定 JS 中的正确术语是什么,但这个例子应该很清楚)
我尝试使用传播语法。见下文。
我有一个 File-实例:
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 是一个文件实例。我假设扩展运算符对 class 实例的工作方式与对对象的工作方式不同?如何实现合并的 object?
更多非必要信息
FileReader
是在 redux 中间件中实现的,并在完成读取后以 { preview: '1234..ef', text: 'Lorem ipsum'}
对象作为有效负载调度新操作。
- 我正在使用
preview
字段将内容映射到文件,并希望 return 在 "files"-reducer 中使用类似以下内容的合并对象:return files.map(file => file.preview !== payload.preview? file: {...file, text: payload.content}
你可能只需要做这样的事情...
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 有一个可以用于此的选择功能。
循环遍历可枚举属性的传播语法。正如您所看到的,下面的代码表明 File 对象的 name
属性 不可枚举。因此,获得这些属性的唯一方法是一项一项。
document.querySelector('input').addEventListener('change', e => {
const file = e.target.files[0];
console.log(file.propertyIsEnumerable('name'));
});
<input type="file">
要合并一个class实例和一个对象,在ES6中,你可以使用Object.assign()来合并对象中的所有属性并保持class实例的原始原型.扩展运算符仅合并所有属性,但不合并原型。
在你的情况下,尝试:
const mergedObject = Object.assign(file, contentObject)
请记住,在这种情况下,您的原始文件对象将被更改。
您可以沿着原型链向上走,创建一个环绕 class 实例的 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());
我想将对象的 properties/values 与 class 实例合并。 (我不确定 JS 中的正确术语是什么,但这个例子应该很清楚)
我尝试使用传播语法。见下文。
我有一个 File-实例:
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 是一个文件实例。我假设扩展运算符对 class 实例的工作方式与对对象的工作方式不同?如何实现合并的 object?
更多非必要信息
FileReader
是在 redux 中间件中实现的,并在完成读取后以{ preview: '1234..ef', text: 'Lorem ipsum'}
对象作为有效负载调度新操作。- 我正在使用
preview
字段将内容映射到文件,并希望 return 在 "files"-reducer 中使用类似以下内容的合并对象:return files.map(file => file.preview !== payload.preview? file: {...file, text: payload.content}
你可能只需要做这样的事情...
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 有一个可以用于此的选择功能。
循环遍历可枚举属性的传播语法。正如您所看到的,下面的代码表明 File 对象的 name
属性 不可枚举。因此,获得这些属性的唯一方法是一项一项。
document.querySelector('input').addEventListener('change', e => {
const file = e.target.files[0];
console.log(file.propertyIsEnumerable('name'));
});
<input type="file">
要合并一个class实例和一个对象,在ES6中,你可以使用Object.assign()来合并对象中的所有属性并保持class实例的原始原型.扩展运算符仅合并所有属性,但不合并原型。
在你的情况下,尝试:
const mergedObject = Object.assign(file, contentObject)
请记住,在这种情况下,您的原始文件对象将被更改。
您可以沿着原型链向上走,创建一个环绕 class 实例的 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());