微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

javascript – 使用spread运算符的ES6对象克隆也在修改输入

我有一个相当深的接口声明,看起来像这样:

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 TWO’也在改变from function参数,即使在’LINE ONE’我正在做我认为是深度克隆的东西.

我知道情况就是这样,因为如果我将’LINE ONE’更改为:

// super hard core deep cloning
let job: Types.Job = JSON.parse(JSON.stringify(from));

……一切都按预期工作. from没有变异,生成的JobUpdatedMessage是预期的,并且StartPipeline的事件参数没有从event.Job中删除一堆属性.

我在这方面花费了数小时的努力,包括重新学习我认为我使用扩展运算符在Es6中克隆对象的所有知识.

为什么’LINE ONE’也会改变输入?

解决方法:

Spread运算符执行与Object.assign()相同的浅层克隆

Shallow-cloning (excluding prototype) or merging of objects is now
possible using a shorter syntax than Object.assign().

Spread operator

理解扩展运算符和浅层克隆的示例.

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属性更改会影响父属性,因为它处于深层次并且仍然是引用.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐