[英]ES6 object cloning using spread operator is modifying input too
我有一個相當深的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庫)
該生產線的市場“兩線”也變異了from
功能參數,即使在“行一個”我在做什么,我認為是一個深克隆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'也會改變輸入?
Spread運算符執行與Object.assign()
相同的淺層克隆
現在可以使用比Object.assign()更短的語法來進行淺層克隆(不包括原型)或合並對象。
理解擴展運算符和淺層克隆的示例。
let obj = { 'a': { 'b' : 1 },'c': 2} let copy = {...obj} copy.c = 'changes only in copy' //shallow-cloned copy.ab = 'changed' // still reference console.log('original\\n',obj) console.log('\\ncopy',copy)
使用spread operator
對象進行shallow cloned
因此所有第一級屬性都將成為副本,而所有更深層次的屬性仍將保留為references
。
正如您在示例中看到的那樣, c
屬性不會影響原始對象,因為它是一個第一級深度,另一方面b
屬性更改會影響父屬性,因為它處於深層次並且仍然是引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.