Как предотвратить ссылки из построенного объекта обратно в проект, например исходный объект, который был назначен прежней целевой структуре?

Это пример из игры, похожей на покемонов. Я создаю объект, и внутри него я пытаюсь создать новый объект «en» и «to», то есть две разные атаки. Проблема в том, что когда я пытаюсь что-то отредактировать в любом из объектов атаки ("en" и "two"), изменение происходит с каждым покемоном с таким же именем. Этого не происходит со «здоровьем», поэтому я думаю, что проблема в this.en = new Object;.

Это код для создания покемонов.

function Fakemon(_navn, _type, _attackPower, _src,
    _1Navn, _1Force, _1Antall_, _2Navn, _2Force, _2Antall) {
    this.navn = _navn;
    this.type = _type;
    this.attackPower = _attackPower;
    this.src = _src;

    this.en = new Object;
    this.en.navn = _1Navn;
    this.en.force = _1Force;
    this.en.antall = _1Antall_;

    this.to = new Object;
    this.to.navn = _2Navn;
    this.to.force = _2Force;
    this.to.antall = _2Antall;

    this.health = 1000;

    console.info(this.en);
    this.pushFakemon = function() {
        fakemonSamling.push(this);
    }
    this.pushFakemon();
}

const fakemon1 = new Fakemon("BatCat", "Flying", [10, 50], ["batFront.png", "batBack.png"], "Blood Suck", [25, 38, 60], 10, "Wing Slap", [10, 17, 25], 20);
const fakemon2 = new Fakemon("Muffin Time", "Normal", [15, 45], ["cupcakeFront.png", "cupcakeBack.png"], "Frosting cover", [10, 17, 25], 20, "Cake stomp", [40, 50, 60], 5);

Это код для выдачи трех покемонов каждому игроку.

for (let i = 0; i < 3; i++) {
        var temp1 = new Object;
        player1.push(Object.assign(temp1, randomFakemon()));
        var temp2 = new Object;
        player2.push(Object.assign(temp2, randomFakemon()));
    }

Можете ли вы поделиться кодом, который вы используете, когда «пытаетесь что-то отредактировать в любом из объектов атаки»?

gui3 16.05.2022 13:40

Нет, проблема не в вашем конструкторе. Скорее дело в том, что randomFakemon() может возвращать фейкмон такой же несколько раз, вместо того, чтобы каждый раз создавать новый.

Bergi 16.05.2022 14:13

Кстати, использование new Object довольно однообразно, лучше использовать литералы объектов

Bergi 16.05.2022 14:16

Еще одна мысль ... в случае, если OP не может выразить намерение функции, особенно функции конструктора, возможно, с 3 или 4 параметрами, OP может подумать об использовании от 1 до 3 объектов конфигурации в качестве параметров. Для варианта использования OP тем более, что все аргументы просто назначаются как общедоступные свойства... подумайте о Object.assign(this, configA, { en: ...configB, to: ...configC });

Peter Seliger 16.05.2022 16:46

... обзор кода, часть 2 ... реализация метода pushFakemon (либо собственного, либо прототипа) необязательна. Вместо этого отправьте только что созданный экземпляр напрямую.

Peter Seliger 16.05.2022 16:55

3/4 ... также, и Берги уже касался темы, в случае, если randomFakemon() возвращает случайным образом Рекомендации из коллекции фейкмонов под названием fakemonSamling, Object.assign не защищает эти ссылки от дальнейших мутаций. С другой стороны, назначение истинного экземпляра Fakemon через Object.assign другому (даже пустому) объекту делает последний не экземпляром Fakemon. Он останется поверхностной копией, объектом, который содержит ссылки на более глубокие вложенные свойства своего Fakemon исходного объекта экземпляра.

Peter Seliger 16.05.2022 17:58

4/4 ... И использование structuredClone также не решает проблему, поскольку можно потерять так или иначе необязательный метод pushFakemon. Таким образом возникает вопрос... Для чего же нужен конструктор. Здесь нет ни прототипных методов, ни подклассов. Мое последнее предложение... "Используйте фабрику фейкмонов".

Peter Seliger 16.05.2022 18:05

Использование StructuredClone сработало, спасибо :)

Kristian Gjertsen 18.05.2022 00:09
"Использование структурированного клона сработало" ... Должно быть, были и другие изменения. Вызов structuredClone завершится ошибкой (вызовет исключение) с экземплярами исходной функции конструктора Fakemone OP из-за реализации метода pushFakemon.
Peter Seliger 18.05.2022 01:27
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
9
37
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По-видимому, у ОП были проблемы с двумя вариантами дизайна.

player1.push(Object.assign(temp1, randomFakemon())); ... выбирает случайный истинный экземпляр Fakemon из коллекции фейкмонов ОП.

Object.assign рассматривает источник как обычный объект. Он присваивает только перечисляемые собственные свойства исходного объекта целевому объекту. Таким образом, вложенные свойства непримитивный по-прежнему будут содержать ссылки на свои источники. Целевой объект также не изменит свой тип. Это не будет вдруг экземпляром Fakemon.

Только что описанную проблему можно решить с помощью надежной собственной реализации функции clone или, если она доступна, с помощью структурированный клон.

При подходе клонирования конструктор, хотя и создает собственные типы, но не расширяет/подкласс и не реализует прототипные методы, становится бесполезным.

Единственный (собственный) метод из примера кода OP в любом случае необязателен, поэтому предлагаемое решение — это рефакторинг кода по отношению к функции Фабрика фейкмонов и использование clone функциональность.

function createFakemon(base, attack1, attack2) {
  const fakemon = Object.assign({
      health: 1000,
    }, base, {
      en: { ...attack1 },
      to: { ...attack2 },
    });
  fakemonCollection.push(fakemon);

  return fakemon;
};
const fakemonCollection = []; // Dansk: fakemon samling.

function getRandomFakemon() {
  return fakemonCollection[
    Math.floor(Math.random() * fakemonCollection.length)
  ];
}

const cloneDataStructure = (
  ('function' === typeof structuredClone) && structuredClone ||
  (value => JSON.parse(JSON.stringify(value)))
);

const fakemon1 = createFakemon({
  navn: 'BatCat',
  type: 'Flying',
  attackPower: [10, 50],
  sources: ['batFront.png', 'batBack.png'],
}, {
  navn: 'Blood Suck',
  force: [25, 38, 60],
  antall: 10,
}, {
  navn: 'Wing Slap',
  force: [10, 17, 25],
  antall: 20,
});
const fakemon2 = createFakemon({
  navn: 'Muffin Time',
  type: 'Normal',
  attackPower: [15, 45],
  sources: ['cupcakeFront.png', 'cupcakeBack.png'],
}, {
  navn: 'Frosting cover',
  force: [10, 17, 25],
  antall: 20,
}, {
  navn: 'Cake stomp',
  force: [40, 50, 60],
  antall: 5,
});

// create a(ny) player's fakemon as true clone
// of a randomly picked `fakemonCollection` item.
const playerFakemon = cloneDataStructure(
  getRandomFakemon()
);
// change a single attack value.
playerFakemon.en.navn = 'Foo Bar';
/*
  Thus instead of ...

  player1.push(Object.assign(temp1, randomFakemon()));

  ... one now would do ...

  player1.push(cloneDataStructure(getRandomFakemon()));
*/
console.info({
  // no mutation at any of the collection's fakemon items.  
  fakemonCollection,
  // single different value for `playerFakemon.en.navn`.
  playerFakemon,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

Это лучший подход, чем мой, но изменение Object.assign на StructuredClone помогло.

Kristian Gjertsen 18.05.2022 00:11

@KristianGjertsen ... «изменение Object.assign на StructuredClone помогло» ... Сомневаюсь. Определенно были и другие изменения. Вызов structuredClone завершится ошибкой (вызовет исключение) с экземплярами исходной функции конструктора Fakemone OP из-за реализации метода pushFakemon.

Peter Seliger 18.05.2022 01:29

Другие вопросы по теме