我有一个相当深的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
}
该接口(interface)的大部分成员本身就是接口(interface),一般架构遵循使用枚举、字符串、数组和更多接口(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
函数参数,即使在“LINE ONE”上我正在做我认为是 from
的深度克隆.
我知道是这种情况,因为如果我将“LINE ONE”更改为:
// super hard core deep cloning
let job: Types.Job = JSON.parse(JSON.stringify(from));
...一切都按预期进行。 from
没有突变,结果 JobUpdatedMessage
符合预期,StartPipeline
的 event
参数没有从 event.Job
中删除一堆属性.
我为此苦苦挣扎了几个小时,包括重新学习我认为我知道的关于使用扩展运算符在 Es6 中克隆对象的所有知识。
为什么“LINE ONE”也会改变输入?
最佳答案
展开运算符与 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
属性更改会影响父对象属性,因为它处于深层次并且仍然是引用。
关于javascript - 使用扩展运算符的 ES6 对象克隆也在修改输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54542400/