0

I was working on a jQuery plugin where I did something like this:

 function MyPlugin() { ... }

 MyPlugin.prototype = {
     foo: function() { ... },
     bar: function() { ... },
     baz: function() { ... }
 }

Now the foo function ran a loop, which called the bar function:

 foo: function() {
     for (i in this.values) {
         var result = this.bar(this.values[i]);
     }
 },


 bar: function(value) {
     var res = '';
     for (i in value) {
         // do something
     }
     return res;
 }

Now please ignore what the functions are actually doing here, the focus is the loop itself. I've noticed that, since I'm using i inside bar - it changes the value of i for foo as well.

I've read about javascript scoping before, but still the example really confuses me. Why is i slipping between functions like that? How does it make any sense? I ended up just using a different variable, but I'm wondering what's the right way to avoid this sort of problem.

Thanks in advance!

Community
  • 1
  • 1
yuvi
  • 18,155
  • 8
  • 56
  • 93

2 Answers2

2

The variable is not only slipping between the functions, it's everywhere. As you haven't declared the variable in the functions, you are using a global variable.

Declare the variable in each function, and each function will have its own variable and leave the global scope alone:

foo: function() {
  for (var i in this.values) {
     var result = this.bar(this.values[i]);
  }
},


bar: function(value) {
  var res = '';
  for (var i in value) {
     // do something
  }
  return res;
}
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • I kind of intuitively guessed that using `var` would solve it, but isn't it weird that if you don't declare it it would automatically turn into a global variable? – yuvi Jun 09 '14 at 20:20
  • @yuvi: The script engine couldn't know if you wanted to create a local variable or if you wanted to use a global variable. If it's not declared locally, it has to be global. If it would create a local variable if there doesn't exist a global variable, then the code would behave differently depending on the situation, which would be hard to follow. – Guffa Jun 09 '14 at 20:39
  • But normally, using an undeclared variable means an error. When using a `for (i in obj)` loop that's somehow fine and automatically a global? I find that very inconsistent, no? – yuvi Jun 10 '14 at 06:02
  • 1
    @yuvi: Javascript was originally intended for doing small tasks in the browser, not building large applications. It was more important that you could just put something in a variable and use it somewhere else without worrying about scope, than programming concepts like encapsulation. – Guffa Jun 10 '14 at 08:06
  • Thanks, it really clears things up for me. It seems that javascript went further than anyone imagined it would. – yuvi Jun 10 '14 at 09:13
1

Since you did not prefix the initial assignment of the i variable with the var directive, JavaScript creates a global variable, which is shared between the foo and bar function calls. Instead, you want:

for (var in in this.values) { ... }

If this.values is an Array, don't use a for-in loop. Use a plain vanilla for loop to avoid looping over the "length" property as well:

for (var i = 0; i < this.values.length; i++) { ... }
Greg Burghardt
  • 17,900
  • 9
  • 49
  • 92