简体   繁体   中英

Property returns undefined even though the value exists…?

I have this object resources :

var resources = { //Handles resources of all kinds.
    number: 100,
    money: 23000000,
    science: 1000,
    popularity: {
        amount: 0,
        upgProd: 0 //Amount produced from upgrades.
    }

};

This looks like a normal object. However, I'm trying to display a certain quantity popularity . Every time I try to display it, I get a NaN instead of a number. I try to return console.log(resources.popularity.upgProd); but I still end up getting an undefined . I have no clue why since I define the variable but I still get undefined... No errors in the IDE or in the console, just undefined when I console.log().

EDIT: Here is some surrounding context... this is my update function, that updates every 1/7 second. ALSO, the first value of resources.popularity.upgProd is 0, then the next become NaN.

function update() {
buffer++;
if (buffer == 35) {
checkVisibilityOnBuildings();
checkVisiblityOnUpgrades();
checkVisibilityOnResources();
buffer = 0;
} // Every 5 seconds (35 ticks) visibility will be checked and updated.

    /* Number increasing. A bit tedious but no other way to do it yet. */
    resources.number += 
   ((BUILDINGS[0].numProdBase * BUILDINGS[0].count) + //Now it's easy! Just do UPGRADES[n+1] for the next building.
    (BUILDINGS[1].numProdBase * BUILDINGS[1].count) +
    (BUILDINGS[2].numProdBase * BUILDINGS[2].count) +
    (BUILDINGS[3].numProdBase * BUILDINGS[3].count));
    //Science gained per tick. Var used to make the "scienceProductionTotalDisp" work properly
    var scienceTotalPerTick = 
    (BUILDINGS[2].sciProdBase * BUILDINGS[2].count) + 
    (BUILDINGS[3].sciProdBase * BUILDINGS[3].count);

    resources.science += scienceTotalPerTick;



    //Display vars for html so that rounding errors don't happen.
    var numDisp = Math.floor(resources.number);
    var sciDisp = Math.floor(resources.science * 100) / 100;
    var popDisp = Math.floor(resources.popularity.amount * 100) / 100;
    console.log(Number(resources.popularity.upgProd));
    var moneyTotalPerTick = Math.pow(resources.number, (1/(player.moneyRatio))) + 1; //Cash flow per 143ms (7n for total / sec ish)
    var popularityTotalPerTick = (Number(resources.popularity.upgProd)) + 0;
    resources.popularity += popularityTotalPerTick;
    console.log(resources.popularity.upgProd);
    resources.money += moneyTotalPerTick;
    getId('moneyProductionTotalDisp').innerHTML = numFormat(Math.floor(moneyTotalPerTick * 7));
    getId('moneyDisp').innerHTML = numFormat(Math.round(resources.money * 100) / 100);
    getId('numberDisp').innerHTML = numFormat(numDisp);
    getId('scienceDisp').innerHTML = numFormat(sciDisp);
    getId('popularityDisp').innerHTML = numFormat(popDisp);
    getId('scienceProductionTotalDisp').innerHTML = 
numFormat(Math.floor(scienceTotalPerTick * 700) / 100);
    getId('popularityProductionTotalDisp').innerHTML = 
numFormat(Math.floor(popularityTotalPerTick * 700) / 100);

Thank you!

Here is your problem:

resources.popularity += popularityTotalPerTick;

popularity is an object, and that doesn't do what you want.

Since you overwrite it with the result of an object added by a value, you assign it s string [object Object]9 where the last digit is whatever was in popularityTotalPerTick.

You get NaN (Not a number) since you are using Number(x) in console.log(Number(resources.popularity.upgProd)); . Why are you doing that?

Does getId do a lookup of the element in the dom every time your function is called? Have the object changed or are you querying the DOM for the same element 7 times per second?


Some thoughts about the other tings in your code:

resources.number += // do you really want += here and not just = ?
  BUILDINGS.reduce( (sum, item) => sum + (item.numProdBase * item.count), 0 );

I'm assuming that BUILDINGS is an array with all the buildings, and that you want to calculate the number of all buildings in the array. There is a function for that: reduce that takes two parameters: a function and the start value:

function (sum, item) { return sum + (item.numProdBase * item.count) }

If your aren't familiar with arrow-functions it could be replaced with:

var scienceTotalPerTick = 
    (BUILDINGS[2].sciProdBase * BUILDINGS[2].count) + 
    (BUILDINGS[3].sciProdBase * BUILDINGS[3].count);

var scienceTotalPerTick =
  BUILDINGS.slice(2,4).reduce( (sum, item) => sum + (item.sciProdBase * item.count), 0);

I'm not sure why you are only doing it for two buildings, and not for all, but you could use reduce here too, with slice

 var scienceTotalPerTick = BUILDINGS.slice(2,4).reduce( (sum, item) => sum + (item.sciProdBase * item.count), 0); 

Notice that the parameters to slice is begin to end ( end not included ), therefor 2,4 gives you element 2 and 3.


With this part of the code...

 getId('moneyProductionTotalDisp').innerHTML = numFormat(Math.floor(moneyTotalPerTick * 7)); getId('moneyDisp').innerHTML = numFormat(Math.round(resources.money * 100) / 100); getId('numberDisp').innerHTML = numFormat(numDisp); getId('scienceDisp').innerHTML = numFormat(sciDisp); getId('popularityDisp').innerHTML = numFormat(popDisp); 

I assume that getId is a function that fetches the element via document.getElementById or document.querySelector , and that you do this for every frame, and you get the same element every time. Now imagine that the element is a box, and that I demand that you get it from the warehouse that is on the other side of the world. When you deliver the box, I open the box, replace the contents and send it back to the warehouse on the other side of the world. When you come back, I demand that you get the same box again, and you now have to travel to the other side of the world once again... and when you come back the story repeats...

My point here is that it is very wasteful to travel to the other side of the world each time to get the same box, or to get the same element from the DOM at each update. You could cache the elements by looking them up ONE time before you start the update, and you can put the result in an object:

function getElementsToUpdate() {
  return [
    'moneyProductionTotalDisp',
    'moneyDisp',
    'numberDisp',
    'scienceDisp',
    'popularityDisp',
    // and so on....
  ].reduce(
    (out, id) => { out[id] = getId(id); return out; }, {}
  )
}

If you name the property in the object the same as the id, you can put all the names in an array, and then reduce it to an object. This saves you some typing, and hard to find bugs because of a misspelled name:

 function getElementsToUpdate() { return [ 'moneyProductionTotalDisp', 'moneyDisp', 'numberDisp', 'scienceDisp', 'popularityDisp', // and so on.... ].reduce( (out, id) => { out[id] = getId(id); return out; }, {} ) } 

NOTE: This function should be run ONE time at startup, not for every update of the frame. It returns an object with the elements.

Somewhere in your code I assume that you use setInterval to call your update function. Since setInteval can take extra parameters that will be given to the called function, you can do something like this:

 var timerHandle = setInterval( update, 1000/7, getElementsToUpdate() ); 

where update is your function, 1000/7 gives you the interval for 7 times a second, and getElementsToUpdate is the function that makes the time-expensive call to get the elements from the DOM one time.

You need to change the update function to take a parameter (the name is not important, but should be short and descriptive, so I use elem ). This is the object that getElementsToUpdate() have returned with all the html-elements.

 function update(elem) { // ... your code .... // Old code, that makes an expensive lookup into the DOM, and start a HTML parser. // getId('numberDisp').innerHTML = numFormat(numDisp); // New code, that gets the pre-looked up element and set the text. elem.numberDisp.textContent = numFormat(numDisp); } 


I'm not a fan of using .innerHTML when it isn't html that is inserted. Always use .textContent instead, if is isn't html. In this case it would be better if you use the output -element, and set the .value property.

尝试使用console.log(resources["popularity"]["upgProd"])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM