0

I am busy creating an object. The end goal is that each evolution will link to a creature in another object.

let fresh = {
  botamon: {
    name: "Botamon",
    image: new URL("https://www.grindosaur.com/img/games/digimon-world/digimon/12-botamon.jpg"),
    stage: "Fresh",
    type: "Data"
  },
}

let inTraining = {
  koromon: {
    name: "Koromon",
    image: new URL("https://www.grindosaur.com/img/games/digimon-world/digimon/50-koromon.jpg"),
    stage: "In-Training",
    type: "Data",
    preDigivolution: fresh.botamon,
    line: rookie.agumon
  },
}

let rookie = {
  agumon: {
    name: "Agumon",
    Image: new URL("https://www.grindosaur.com/img/games/digimon-world/digimon/1-agumon.jpg"),
    Stage: "Rookie",
    Type: "Vaccine",
    PreDigivolution: inTraining.koromon
  },
}

The line: rookie.agumon throws an error, because the way this works is that koromon evolves to agumon so I have no idea why its throwing an error, the error is

digimon-digivolution.js:41 Uncaught ReferenceError: Cannot access 'rookie' before initialization

is it incorrect to call rookie.agumon in the koromon object?

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
FrontDevUp
  • 27
  • 5
  • 1
    You cannot access an object before it's created. The line `rookie.agumon` is before the object `rookie` is there. You're trying to put the fish in the frying pan before you've acquired any fish. So, yes - it is incorrect to try and break the linear time flow by trying to use something that will only exist in the future. – VLAZ Nov 27 '22 at 10:26
  • 1
    You can use a [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) to grab the value from the associated value at the time of accessing the property. Otherwise, you can declare your objects and then update them after they've been declared. – Nick Parsons Nov 27 '22 at 10:30
  • 4
    Related: [Self-references in object literals / initializers](/q/4616202/4642212). There are a few solutions to this, but the one that makes the most sense is to just do separate assignments after all objects are initialized. – Sebastian Simon Nov 27 '22 at 10:33
  • For your use case, it might also make sense to just declare a property with the *name* (as a string) of the other creature, then look that up on the respective level object. – Bergi Nov 27 '22 at 11:07

1 Answers1

2

You are trying to resolve an identifier before its declaration. The variable is said to be in a TDZ (temporal dead-zone).

Trying to swap the declaration/initialization statements would still result in a similar ReferenceError: The identifier's resolution would still happen in its TDZ. With just the two statements it is impossible to create a circular reference.

Instead, you can separate the initializations into individual statements as @SebastianSimon suggested, where you assign the references afterwards. Example:

// Initialize separately
const koromon = {/*...*/};
const agumon = {/*...*/};

// Then assign to each other
koromon.digivolution = agumon;
agumon.preDigivolution = koromon;

Or you can use getters to avoid the problem of the TDZ, as @NickParsons suggests. This works because only upon access are we resolving the identifier, at which time we have already left its TDZ. Example:

const koromon = {
  name: "Koromon",
  get digivolution() { return agumon; }
};
const agumon = {
  name: "Agumon",
  get preDigivolution() { return koromon; }
};

console.log("Koromon's digivolution is:", koromon.digivolution.name);
Oskar Grosser
  • 2,804
  • 1
  • 7
  • 18