2

Why can't you access the object property with dot notation inside a loop?

var rockSpearguns = {
  Sharpshooter: {barbs: 2, weight: 10, heft: "overhand"},
  Pokepistol: {barbs: 4, weight: 8, heft: "shoulder"}
};
function listGuns(guns) {
  for ( var gun in guns ) {
    console.log("Behold! " + gun + ", with "  + gun.heft + " heft!");
  }
}
listGuns(rockSpearguns);

This produces this output:

Behold! Sharpshooter, with undefined heft!
Behold! Pokepistol, with undefined heft!

But, if I change gun.heft to guns[gun].heft, I get the correct output.

Behold! Sharpshooter, with overhand heft!
Behold! Pokepistol, with shoulder heft!

It seems that I can get the name of the gun inside the loop, but I can't call a method on it to get the heft. Why?

In, say Ruby, inside a loop, the the current item at index is usually an object. No so here?

starsplusplus
  • 1,232
  • 3
  • 19
  • 32
Jumbalaya Wanton
  • 1,601
  • 1
  • 25
  • 47
  • You're trying to get the property of string, not the object it maps to. – Barmar Jan 15 '14 at 12:11
  • And the answer is: read the friggin manual. That's how JavaScript works. – kuroi neko Jan 15 '14 at 12:12
  • 1
    Check out this question for information about proper way to get the for-each loop working in js: http://stackoverflow.com/questions/9329446/for-each-in-an-array-how-to-do-that-in-javascript – vbo Jan 15 '14 at 12:12
  • 2
    Javascript is not Ruby or PHP. `for-in` is not like PHP `foreach`. – Barmar Jan 15 '14 at 12:13
  • @vbo: Except that question is about arrays rather than objects. – T.J. Crowder Jan 15 '14 at 12:14
  • You topic description is weird 'Calling methods on objects' ? – Dhanu Gurung Jan 15 '14 at 12:14
  • @ram In addition to not knowing how `for-in` works, he also doesn't know the difference between calling methods and accessing properties. – Barmar Jan 15 '14 at 12:15
  • @ Jumbalaya: *"It seems that I can get the name of the gun inside the loop, but I can't call a method on it to get the heft. Why?"* What makes you say that? Nothing in your code is trying to call a method on the gun objects. If it were, `guns[gun].methodName();` would work, for the same reason `guns[gun].heft` works. – T.J. Crowder Jan 15 '14 at 12:16
  • @Barmar: I can agree with not knowing how accessing element in 'for-in ' works but definitely can't tolerate with calling methods with the kind of object he described. :) – Dhanu Gurung Jan 15 '14 at 12:17
  • @T.J.Crowder indeed, so it's not an answer but comment. I am just suggesting Jumbalaya to learn more about looping in javascript in general. – vbo Jan 15 '14 at 12:18
  • @everyone I'm just learning JavaScript. Please take it easy. – Jumbalaya Wanton Jan 15 '14 at 12:20
  • @ram your description of a question title is weird. "Topic description?" – Jumbalaya Wanton Jan 15 '14 at 12:30

6 Answers6

2

But, if I change gun.heft to guns[gun].heft, I get the correct output.

...

It seems that I can get the name of the gun inside the loop, but I can't call a method on it to get the heft. Why?

There's no code in your question trying to use a method on the objects. If there were, it would work, for the same reason that guns[gun].heft works:

// The function we'll make a method
function getGunHeft() {
    return this.heft;
}

// Create the objects with that function on them as a method
var rockSpearguns = {
  Sharpshooter: {barbs: 2, weight: 10, heft: "overhand", getHeft: getGunHeft},
  Pokepistol: {barbs: 4, weight: 8, heft: "shoulder", getHeft: getGunHeft}
};

// Use the method
function listGuns(guns) {
  for ( var gun in guns ) {
    console.log("Behold! " + gun + ", with "  + guns[gun].getHeft(); + " heft!");
  }
}
listGuns(rockSpearguns);

"Methods" in JavaScript are just functions attached to object properties (either directly, as above, or through the object's underlying "prototype" object). So if you can access an object property, you can call the function it refers to.

When you call a function as part of the same expression where you get the function from an object property, the JavaScript engine sets this to refer to the object during the call to the function. That's why I used this.heft above to access the heft property of the gun.

More in this article on my blog: Mythical Methods

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

When you're navigating through an object with a for-each loop, you have to use guns[gun] to access said object, presuming you're object is guns and you're looping through to find gun.

mjkaufer
  • 4,047
  • 5
  • 27
  • 55
1

gun here would be the index of your loop. Try:

function listGuns(guns) {
  for ( var gun in guns ) {
    console.log("Behold! " + gun + ", with "  + guns[gun].heft + " heft!");
  }
}
Jonathan Petitcolas
  • 4,254
  • 4
  • 31
  • 42
1

Since you're relating the loop to Ruby, I'll show the comparison:

JS:

for (gun in guns)

is analogous to Ruby:

guns.keys.each do |gun|

In both cases, gun is set to the property name, not the property value. To get the corresponding value, you have to access guns[gun] in both languages.

In Ruby you would probably write the loop as:

guns.each do |gunname, gun|

so that you can access properties of the gun object directly. Javascript doesn't have this operation built in, but it's simply to write a function that does it.

Barmar
  • 741,623
  • 53
  • 500
  • 612
1

gun.heft doesn't work inside for-in loop since gun is local variable inside for-in loop which is of string type and placeholder for the key of Object being iterated.

var guns = {
  Sharpshooter: {barbs: 2, weight: 10, heft: "overhand"},
  Pokepistol: {barbs: 4, weight: 8, heft: "shoulder"}
};
for(var gun in guns){ 
  console.log(typeof(gun)) 
}

# print string two times

Hence, you need to use guns[gun] to access particular gun's value. And, guns[gun].heft would work since value at guns[gun] is an object.

Hope it help you to understand more. Click Here to know more on for-in loop.

Dhanu Gurung
  • 8,480
  • 10
  • 47
  • 60
0

For anyone coming here still in 2022 you could also do this:

function listGuns(guns) {
  for (let [gun, value] of Object.entries(guns)) {
    console.log("Behold! " + gun + ", with " + value.heft + " heft!");
  }
}

see source on mdn

Timothy
  • 346
  • 6
  • 18