Joh*_*ing 3 javascript typescript ecmascript-6
我有一个相当深的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
}
Run Code Online (Sandbox Code Playgroud)
这个接口的大多数成员本身就是接口,一般架构遵循这种使用枚举,字符串,数组和更多接口的模式.所有代码都是作为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 };
};
Run Code Online (Sandbox Code Playgroud)
定义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);
}
}
Run Code Online (Sandbox Code Playgroud)
问题是在调用之后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;
};
Run Code Online (Sandbox Code Playgroud)
(我在这里使用lodash库)
线路市场'LINE TWO'也在改变from功能参数,即使在'LINE ONE'我正在做我认为是深刻克隆的东西from.
我知道情况就是这样,因为如果我将'LINE ONE'更改为:
// super hard core deep cloning
let job: Types.Job = JSON.parse(JSON.stringify(from));
Run Code Online (Sandbox Code Playgroud)
......一切都按预期工作.from不被突变,得到的JobUpdatedMessage是如预期,和StartPipeline的event参数不具有一束从除去性能event.Job.
我在这方面花费了数小时的努力,包括重新学习我认为我使用扩展运算符在Es6中克隆对象的所有知识.
为什么'LINE ONE'也会改变输入?
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)Run Code Online (Sandbox Code Playgroud)
使用spread operator对象是shallow cloned这样所有的第一级属性将成为副本,而所有更深层次的属性仍将保留references.
因此,您在示例中看到c属性不会影响原始对象,因为它是一个第一级深度,另一方面,b属性更改会影响父属性,因为它处于深层次并且仍然是引用.
| 归档时间: |
|
| 查看次数: |
119 次 |
| 最近记录: |