Search code examples
javascriptobjectfor-in-loop

How are objects passed into a for...in loop in JavaScript?


I'm new to JavaScript and am learning about for...in loops and objects right now. I'm practicing with the following example:

let spaceship = {
    crew: {
    captain: { 
        name: 'Lily', 
        degree: 'Computer Engineering', 
        cheerTeam() { console.log('You got this!') } 
        },
    'chief officer': { 
        name: 'Dan', 
        degree: 'Aerospace Engineering', 
        agree() { console.log('I agree, captain!') } 
        },
    medic: { 
        name: 'Clementine', 
        degree: 'Physics', 
        announce() { console.log(`Jets on!`) } },
    translator: {
        name: 'Shauna', 
        degree: 'Conservation Science', 
        powerFuel() { console.log('The tank is full!') } 
        }
    }
}; 

for (let crewMember in spaceship.crew){
  console.log(`${crewMember}: ${crewMember.name}`);
}

The output of this code is that the console successfully logs crewMember, but not crewMember.name. crewMember.name is undefined, as in the image. On the other hand, when I change the log statement to this:

for (let crewMember in spaceship.crew){
  console.log(`${crewMember}: ${spaceship.crew[crewMember].name}`);
}

the name of the crew member is logged successfully. I don't understand why I am able to access the crew member without going through the spaceship.crew in the first half of the console.log statement, but in the second half I have to go through spaceship.crew.


Solution

  • As said in the comment. The for...in loop fetches the property name as a key. So it's a placeholder for captain or 'chief officer'. You can access a property via dot notation and bracket notation. Use brackets when using variables:

    so:

    spaceship.crew[crewMember]
    

    That should work. See example:

    let spaceship = {
        crew: {
        captain: { 
            name: 'Lily', 
            degree: 'Computer Engineering', 
            cheerTeam() { console.log('You got this!') } 
            },
        'chief officer': { 
            name: 'Dan', 
            degree: 'Aerospace Engineering', 
            agree() { console.log('I agree, captain!') } 
            },
        medic: { 
            name: 'Clementine', 
            degree: 'Physics', 
            announce() { console.log(`Jets on!`) } },
        translator: {
            name: 'Shauna', 
            degree: 'Conservation Science', 
            powerFuel() { console.log('The tank is full!') } 
            }
        }
    }; 
    
    for (let crewMember in spaceship.crew){
      console.log(`${crewMember}: ${spaceship.crew[crewMember].name}`);
    }

    And as Jared Smith said in his comment, use static methods instead: So put of the objects own enumerable keys in an array using Object.keys and iterate over them using forEach and a Arrow function expression.

    let spaceship = {
        crew: {
        captain: { 
            name: 'Lily', 
            degree: 'Computer Engineering', 
            cheerTeam() { console.log('You got this!') } 
            },
        'chief officer': { 
            name: 'Dan', 
            degree: 'Aerospace Engineering', 
            agree() { console.log('I agree, captain!') } 
            },
        medic: { 
            name: 'Clementine', 
            degree: 'Physics', 
            announce() { console.log(`Jets on!`) } },
        translator: {
            name: 'Shauna', 
            degree: 'Conservation Science', 
            powerFuel() { console.log('The tank is full!') } 
            }
        }
    }; 
    
    //Object.keys | return all the keys
    Object.keys(spaceship.crew).forEach( crewMember => {
      console.log(`${crewMember}: ${spaceship.crew[crewMember].name}`);
    });
    
    //Object.entries | returns objects key and value in an array which you can iterate
      Object.entries(spaceship.crew).forEach( ([crewMemberKey, crewDetails]) => {
        console.log(`${crewMemberKey}: ${crewDetails.name}`);
      });