簡體   English   中英

使用擴展運算符的ES6對象克隆也在修改輸入

[英]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.FilterJobPropertiespayload.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是預期的,並且StartPipelineevent參數沒有從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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM