Javascript снова загадочный? - PullRequest
1 голос
/ 05 июня 2019

У меня есть служба в angular, которая имеет два свойства одного типа. Я нажимаю те же объекты в обратном порядке к ним для тестирования. Но когда я изменяю один из объектов в одном массиве, я вижу, что тот же код меняет тот же объект и во втором массиве. Дело в том, что я даже не упоминаю второй массив в коде. Вот мой класс обслуживания:

class Myservice {

  public meetings : Meeting [];
  public participated : Meeting [];

  constructor(){
      this.meeting = [];
      this.participated = [];
  }

  create(participants, id){

       let meeting : Meeting = {
          id : id,
          participants : participants
       }

      this.meetings.push(meeting);
      this.participated.unshift(meeting);
  }

  addParticipant(userId : number, meetingId : number){
      this.meetings.forEach((meeting : Meeting) => { 
         if(meeting.id == meetingId)  {
           meeting.participants.push(userId)
         } 
      })
  }


}

Интерфейс My Meeting:

interface Meeting {
     id : number;
     participants : Array<number>;
}

Вот как я это проверяю:

describe('My service', () => {

     let provider : Myservice;

     beforeEach( () => {
        provider = new Myservice();
     });

     it('should add a participant to the participants', () => {

        provider.create([2], 1);
        provider.create([2], 2);
        provider.create([2], 3);
        provider.create([2], 4);

        provider.addParticipant(6, 3);

        expect(provider.meetings[2].participants).toEqual([2,6])  
        //expect(provider.participated[1].participants).toEqual([2,6])  

        console.log('meeting par :', provider.meetings[2].participants)
        console.log('Partic par :', provider.participated[1].participants)
     })

Этот тест проходит как надо, но в журналах написано, что:

LOG: 'meeting par :', [2, 6]
LOG: 'Partic par :', [2, 6]

Как мы видим, объект в массиве-участнике также изменился: O. Ребята, вы знаете, почему? Мои версии кармы / жасмина:

"@types/jasmine": "^3.3.13",
"@types/node": "^12.0.4",
"angular2-template-loader": "^0.6.2",
"core-js": "^3.1.3",
"html-loader": "^0.5.5",
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine": "^3.4.0",
"jasmine-spec-reporter": "^4.2.1",
"karma": "^4.1.0",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage-istanbul-reporter": "^2.0.5",
"karma-jasmine": "^2.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.5",
"null-loader": "^2.0.0",
"protractor": "^5.4.2",
"ts-loader": "^3.0.3",
"ts-node": "^3.0.2",
"typescript": "~2.6.2"
"@angular/compiler": "5.2.11",
"@angular/compiler-cli": "5.2.11",
"@angular/core": "5.2.11",

Редактировать. Новая форма функции create также дает такой же ответ:

  create(participants, id){
      this.meetings.push({
         id : id,
         participants : participants
      });
      this.participated.unshift({
         id : id,
         participants : participants
      });

  }

Ответы [ 2 ]

3 голосов
/ 05 июня 2019

Итак, глядя на ваш код:

create(participants, id){
  let meeting : Meeting = {
    id : id,
    participants : participants
  }

  this.meetings.push(meeting);
  this.participated.unshift(meeting);
}

Вы создаете один объект и помещаете этот один объект в два разных места.Когда вы помещаете эти объекты в эти массивы, вы даете этим массивам не копию объекта, а ссылку на него.

В памяти существует только один meeting, и он имеет ссылку внутри массивов meetings и participated.Поэтому, когда вы получаете meeting внутри addParticipant, вы изменяете этот единственный объект meeting.

Это не таинственный JavaScript, его можно рассматривать как массивы, имеющие ссылку на объект, а не на сам объект.

Кроме того, массив participants также является объектом с двумя ссылками.,Если вы хотите, чтобы копия массива была в каждом экземпляре, вы можете распространить ее в новом массиве.Пример копирования массива с использованием синтаксиса распространения и использования синтаксиса распространения в массивах можно увидеть здесь .

Чтобы решить эту проблему, вы можете каждый раз создавать новый объектВы нажимаете на массив, расширяя массив участников в новый, чтобы создать новый массив участников:

  this.meetings.push({id: id, participants: [...participants]});
  this.participated.unshift({id: id, participants: [...participants]});

Или вы можете использовать сокращение объекта, так как они используют одинаковые имена для id свойство:

  this.meetings.push({id, participants: [...participants]});
  this.participated.unshift({id, participants: [...participants]});

Таким образом, каждый массив имеет ссылку на отдельный объект, и каждый массив participants также уникален в пределах объекта Meeting.

Надеюсь, чтоhelp!

Редактировать 1: добавлены дополнительные сведения после обнаружения более одной проблемы с предоставленным кодом

Редактировать 2: добавлены ресурсы в спредоператор, и использовать его на массивах

3 голосов
/ 05 июня 2019

Вы толкаете один и тот же объект по его ссылке на оба массива. Это причина вашей проблемы. Вы должны либо создавать новый объект для каждого нажатия, либо клонировать объект.

Обновление # 1

Вторая потенциальная проблема, которую я обнаружил, это ваш массив participants.

В JS массивы, а также объекты передаются по ссылке, когда их просто присваивают.

Для простых объектов вы можете использовать Object.assign и для простых массивов [..].slice().

Еще один способ клонирования var a = {...myObj} для объектов и var b = [...myArr] для массивов.

Эти методы предназначены для простого клонирования, которое не позволяет правильно клонировать вложенные объекты / массивы. Для этого вам нужно написать рекурсивную функцию клонирования или использовать cloneDeep из Lodash, который совместим с массивами и объектами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...