boulder_ruby boulder_ruby - 3 months ago 7
Ruby Question

Why does javascript persist local variable reassignments that occur in outside functions without having to capture the return values?

I'm a ruby programmer, so where I come from customarily, if you had something like this:

def addone(x)
x+1
end

def ruby_assignment_behavior(x)
addone(x)
puts "x equals " + x
end


running that last method would result in the following:

ruby_assignment_behavior(1)
#=> "x equals 1"


in javascript, something equivalent to this would return
x equals 2
, I think.

I discovered this unique quality of javascript (relative to ruby) after studying this code (fetches user gps coordinates)

var currPosition;
navigator.geolocation.getCurrentPosition(function(position) {
updatePosition(position);
$("#lat").html(position.coords.latitude;);
$("#lng").html(position.coords.longitude);
};

function updatePosition(position) {
currPosition = position;
}


Why is it that inside of the
getCurrentPosition
function, the
position
variable is updated with the return value of
updatePosition()
, even though
position
is a local variable inside of a closure(?)

ALSO:
I'm curious, in the javascript code sample, is having that
updatePosition
function outside of
getCurrentPosition
necessary, and if so, why is that the case? Does that
currPosition
variable which was defined in the outermost scope somehow carry the reassigned
position
value?

Answer

The two pieces of code are very different. In your Ruby code, you are changing the variable value. Since the variable is local, as you correctly state, the change is not reflected outside the scope.

In your JavaScript code, you are changing the internal state of an object pointed to by the variable. The variable itself does not change.

In this respect, Ruby and JavaScript behave identically.

var a = { count: 0 };
function increment(x) {
  x.count++; // variable changed, changing referenced object state
}
increment(a);
console.log(a.count);
// => 1

is equivalent to

a = { count: 0 }
def increment(x)
  x[:count] += 1 # variable changed, changing referenced object state
end
increment(a)
puts a[:count]
# => 1

while

var b = { count: 0 };
function increment(x) {
  x = { count: x.count + 1 }; // changing variable's reference
}
increment(b);
console.log(b.count);
// => 0

is equivalent to

b = { count: 0 }
def increment(x)
  x = { count: x[:count] + 1 } # changing variable's reference
end
increment(b)
puts b[:count]
# => 0

var currPosition outside the function declares the variable currPosition to be in the scope that is wider than the function, kind of but not quite like using $currPosition in Ruby. This allows the function to assign the value to a variable that will be visible oustide it:

var c = 0; // outer scope
function update() {
  c = 1;
}
update();
console.log(c);
// 1

but

function update() {
  var d = 0; // inner scope
  d = 1;
}
update();
console.log(d);
// undefined

In Ruby, variables are not allowed to jump function (method) scopes like that, but you can use @a, @@a or $a to access an outer scope (instance, class or global):

def update
  c = 1 # local scope
end
update
puts c
# => Error

but

@d = nil # instance scope
def update
  @d = 1
end
update
puts @d
# => 1

However, there are similar scope effects with blocks in Ruby as with functions in JavaScript:

e = nil # outer scope
1.times do
  e = 1
end
e
# => 1

but

1.times do
  f = 1 # inner scope
end
f
# => Error