JavaScript: Global By Default

September 1, 2008

Here's a very simple JavaScript function that prints the sum of its arguments:

function sum() {
  s = 0;
  for(i=0; i < arguments.length; i++) {
    s += arguments[i];
  }
  return s;
}
document.write('sum = '+sum(1, 2, 3));

Looks simple enough. Translated directly into non-idiomatic Ruby, that would be:

def sum(*args)
  s = 0
  i = 0
  while i < args.length
    s += args[i]
    i += 1
  end
  s
end

So let's say you now want your sum function to return the sum of the factorial of each number. No problem:

function sum() {
  s = 0;
  for(i=0; i < arguments.length; i++) {
    s += factorial(arguments[i]);
  }
  return s;
}
function factorial(n) {
  f = 1;
  for(i=1; i <= n; i++) {
    f *= i;
  }
  return f;
}
document.write('sum = '+sum(1, 2, 3));

Oops. That prints 1, but the answer we were looking for was 9. Hmmm, let's translate it to ruby and see what we get:

def sum(*args)
  s = 0
  i = 0
  while i < args.length
    s += factorial(args[i])
    i += 1
  end
  s
end
def factorial(n)
  s = 1
  i = 1
  while i <= n
    s = s * i
    i += 1
  end
  s
end
puts sum(1, 2, 3)

Ok, so Ruby gives us 9. So what's up? The truth is that is not a direct translation. Here's the correct translation:

def sum(*args)
  $s = 0
  $i = 0
  while $i < args.length
    $s += factorial(args[$i])
    $i += 1
  end
  $s
end
def factorial(n)
  $s = 1
  $i = 1
  while $i <= n
    $s = $s * $i
    $i += 1
  end
  $s
end
puts sum(1, 2, 3)

This gives us the same result as the flawed JavaScript, which is 1. As you can see, in both functions, the variables s and i are declared as global variables, which you can tell by the $ sigil. But in JavaScript, variables are global by default. That's right, the simple little innocuous-looking i=0 in our JavaScript for loop defines a global variable. Here is the corrected JavaScript version:

function sum() {
  var s = 0;
  for(var i=0; i < arguments.length; i++) {
    s += factorial(arguments[i]);
  }
  return s;
}
function factorial(n) {
  var f = 1;
  for(var i=1; i <= n; i++) {
    f *= i;
  }
  return f;
}
document.write('sum = '+sum(1, 2, 3));

The moral of the story is always prefix your variable declarations with var. If you are a web developer who writes JavaScript and this is news to you, stop what you are doing an read Douglas Crockford's JavaScript: The Good Parts.

Posted in Technology | Tags Javascript

Comments

1.

Good post! I remember the day that I found this out the hard way (some 3 years ago, on my first project involving javascript). It was an unpleasant surprise, to say the least. ; )

# Posted By Ethan Vizitei on Saturday, September 6 2008 at 11:46 AM

Comments Disabled