我有一个很深的interface
声明,看起来像这样:
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 ONE”上,我正在做的是from
的深层克隆,线路市场“ LINE TWO”也正在改变from
函数参数。
我知道是这种情况,因为如果我将“ LINE ONE”更改为:
// super hard core deep cloning
let job: Types.Job = JSON.parse(JSON.stringify(from));
...一切正常。 from
未突变,得到的JobUpdatedMessage
符合预期,并且StartPipeline
的{{1}}参数没有从event
中删除一堆属性。
我为此付出了数小时的努力,其中包括重新学习我相信使用散布运算符在Es6中克隆对象的所有知识。
为什么“ LINE ONE”也会同时改变输入内容?
答案 0 :(得分:5)
Spread运算符与Object.assign()
一样进行浅层克隆
现在是对象的浅克隆(不包括原型)或合并 可能使用比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)
使用 spread operator
对象是shallow cloned
,因此所有 第一级 属性将成为副本,而所有 更深级别 属性仍将保留references
。
因此,如您在示例中所见,c
属性不会影响原始对象,因为它是一个第一级深度,另一方面,b
属性更改会影响父属性,因为它的深度很深级别,仍在参考。