Javascript 使用扩展运算符进行ES6对象克隆也在修改输入

Javascript 使用扩展运算符进行ES6对象克隆也在修改输入,javascript,typescript,ecmascript-6,Javascript,Typescript,Ecmascript 6,我有一个相当深的接口声明,看起来像这样: 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 T

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

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上运行)

在代码中的某一点上,我需要制作一份作为函数参数传入的
作业
实例化的深度副本:

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 };
};
问题是在调用
JobUtils.FilterJobProperties
之后,
有效负载.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;
  };
(我正在使用洛达斯图书馆)

line market的“line TWO”也在变异
from
函数参数,尽管在“line ONE”上我所做的是我认为是来自
的深层克隆

我知道这是因为如果我将“第一行”更改为:

// super hard core deep cloning
let job: Types.Job = JSON.parse(JSON.stringify(from));
。。。一切正常<来自
的代码>未发生变异,生成的
作业更新消息与预期一致,并且开始管道的事件参数没有从
事件.作业
中删除一组属性

我为此挣扎了数小时,包括重新学习我认为我知道的关于使用扩展操作符在Es6中克隆对象的所有知识


为什么“第一行”也会改变输入?

扩展运算符的作用与
Object.assign()相同。

对象的浅层克隆(不包括原型)或合并现在正在进行 可能使用比Object.assign()更短的语法

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

let obj={'a':{'b':1},'c':2}
让copy={…obj}
copy.c='仅在副本中更改'//
copy.a.b='changed'//仍然引用
console.log('original\n',obj)

console.log('\ncopy',copy)
这很有意义。为什么这不是我在命令行上运行Node时看到的实际行为<代码>>a={foo:1',bar:{baz:42,bat:{deep:hi'}}}{foo:1',bar:{baz:42,bat:{deep:hi'}}}>b={…a}{foo:1',bar:{baz:42,bat:{deep:hi'}}}@john JohnDibling-一个浅层克隆看起来就像一个深层克隆,直到你改变了一个深层属性。也就是说,当一个值和一个引用解析为同一个值时,你看不到它们之间的区别。嘿,@jonaswillms。我一直在努力回答这个问题。我能说的只是“不是那样”。然而,这一切都是有意义的,我认为这也有助于解释我在前端看到的一些奇怪的行为,它使用NGRX-也充满了
{…obj}
副本。@johndilling我添加了一个例子,你可以看到更多的解释。我希望这会让事情变得更清楚
// super hard core deep cloning
let job: Types.Job = JSON.parse(JSON.stringify(from));