我有一个相当深的接口声明,看起来像这样:
export interface Job {
JobId: JobId; // type JobId = string
UserId: UserId; // type UserId = string
JobName: string;
AudioFile: JobAudioFile; // this is an interface
Status: JobStatus; // this is an enum
Tracks: JobTracks[]; // 'JobTracks' is an enum
Results: JobResults; // this is an interface
Timestamps: JobTimestamps // interface
}
这个接口的大多数成员本身就是接口,一般架构遵循这种使用枚举,字符串,数组和更多接口的模式.所有代码都是作为TypeScript编写的,编译成JS并作为JS上传到AWS. (节点8.10正在AWS上运行)
在代码中的某一点,我需要制作一个作为函数参数传入的Job实例的深层副本:
export const StartPipeline: Handler = async (
event: PipelineEvent
): Promise<PipelineEvent> => {
console.log('StartPipeline Event: %o', event);
const newBucket = await copyToJobsBucket$(event.Job);
await deleteFromOriginalBucket$(event.Job);
console.log(`Job [${event.Job.JobId}] moved to Jobs bucket: ${newBucket}`);
event.Job.AudioFile.Bucket = newBucket;
event.Job.Status = Types.JobStatus.Processing;
// update the job status
// VVV PROBLEM OCCURS HERE VVV
const msg: Types.JobUpdatedMessage = new Types.JobUpdatedMessage({ Job: Object.assign({}, event.Job) });
await Send.to$(event.Job.UserId, msg);
return { ...event };
};
JobUpdatedMessage的定义:
export class JobUpdatedMessage extends BaseMessage {
constructor(payload: { Job: Types.Job }) {
console.log('Incoming: %o', payload);
const copy: object = { ...payload.Job };
// VVV PROBLEM ON NEXT LINE VVV
const filtered = JobUtils.FilterJobProperties(copy as Types.Job);
super(MessageTypes.JobUpdated, filtered);
}
}
问题是在调用JobUtils.FilterJobProperties之后,payload.Job也以不合需要和意想不到的方式发生了变异.
这是JobUtils.FilterJobProperties的实现:
export const FilterJobProperties = (from: Types.Job): Types.Job => {
const fieldsToRemove: string[] = [
'Transcripts.GSTT',
'Transcripts.WSTT',
'Transcripts.ASTT',
'TranscriptTracks',
'Transcripts.Stream.File',
'Transcripts.Stream.State',
'AudioFile.Bucket',
'AudioFile.S3Key',
];
let job: Types.Job = { ...from }; // LINE ONE
fieldsToRemove.forEach(field => _.unset(job, field)); // LINE TWO
return job;
};
(我在这里使用lodash库)
线路市场’LINE TWO’也在改变from function参数,即使在’LINE ONE’我正在做我认为是深度克隆的东西.
我知道情况就是这样,因为如果我将’LINE ONE’更改为:
// super hard core deep cloning
let job: Types.Job = JSON.parse(JSON.stringify(from));
……一切都按预期工作. from没有变异,生成的JobUpdatedMessage是预期的,并且StartPipeline的事件参数没有从event.Job中删除一堆属性.
我在这方面花费了数小时的努力,包括重新学习我认为我使用扩展运算符在Es6中克隆对象的所有知识.
为什么’LINE ONE’也会改变输入?
解决方法:
Spread运算符执行与Object.assign()相同的浅层克隆
Shallow-cloning (excluding prototype) or merging of objects is now
possible using a shorter syntax than Object.assign().
理解扩展运算符和浅层克隆的示例.
let obj = { 'a': { 'b' : 1 },'c': 2}
let copy = {...obj}
copy.c = 'changes only in copy' //shallow-cloned
copy.a.b = 'changed' // still reference
console.log('original\n',obj)
console.log('\ncopy',copy)
使用扩展运算符对象进行浅层克隆,因此所有第一级属性都将成为副本,而所有更深层次的属性仍将保留为引用.
正如您在示例中看到的那样,c属性不会影响原始对象,因为它是一个第一级深度,另一方面b属性更改会影响父属性,因为它处于深层次并且仍然是引用.
原文地址:https://codeday.me/bug/20190827/1745242.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。