views:

67

answers:

3

I was reviewing some today, when I encountered the following convention :

TestParam(1);

function TestParam(p){
    var p = p + 1;
    alert(p);   // alerts '2'
}

Now, obviously, the developer didn't mean to delcare 'p' within the function, instead maybe meaning:

    p = p + 1;

But the code still worked, i.e. the value alerted was "2". So it got me to thinking. What would happen in the following scenario:

var a = 1;
TestParam(a);
alert(a);  // alerts "1"

function TestParam(p){
    var p = p + 1;
    alert(p);   // alerts '2'
}

Again the alerts were as I suspected (as intimated in the comments above). So then I was curious as to what would happen if I used an object:

var a = { b: 1 };
TestParam(a);
alert(a.b);  //alerts 1

function TestParam(p) {
    var p = {b:p.b + 1};
    alert(p.b); //alerts 2
}

So, in this instance, JavaScript has 'remembered' the variable a, even though when it is passed to TestParam as p, p is redeclared.

Now if I were to have done the following within the function, then both alerts would have been "2"

    p.b++;
    //var p = {b:p.b + 1};

I.e. it would have modified the member b of the original object. I get this. Its the previous scenario that baffles me!

I realise this is quite a hypothetical question that is unlikely to carry much real-world usefulness, but it still made me quite curious, as to what is going on in the background, and how exactly JavaScript is scoping and referencing these variables.

Any thoughts?

+4  A: 

Variables are scoped to the enclosing function in JavaScript. And you can declare them as many times as you like.

Objects are not tied to variables; many variables can refer to the same object. (This is sometimes called aliasing.) This is what's happening in your last example. JavaScript has not "remembered" the variable a at all; rather, p and a refer to the same object, so when it is changed via p, you can see those changes via a.

Here's an example that might make more sense to you. Sometimes a person has more than one identity.

var Clark = new Person;
var Superman = Clark;  // Maybe not everybody needs to know these two are
                       // the same person, but they are.
Clark.eyes = "brown";
alert(Superman.eyes);  // should be brown, right?

Knowing the rules of the language you're using has tremendous "real-world usefulness". Not understanding these two rules is the source of a lot of confusion and bugs.

Jason Orendorff
Thanks. I actually undertand the last case above. Its the second last case where the object is redeclared that I am struggling with.
James Wiseman
Ah! In that case `p = p + 1` actually changes *the variable p* to refer to a different number. After you assign to `p`, it no longer refers to the same value as `a`.
Jason Orendorff
Right got you. So, 'a' refers to 'a', and 'p' refers to 'a' until I reassign, in which case, 'a' still refers to 'a' but 'p' refers to something different entirely. Its like breaking the link! Brilliant. Thanks :-)
James Wiseman
+1  A: 

Javascript generally passes all arguments to functions by reference except when the argument is a number or a string. Javascript passes numbers and strings by value which is why your first example works the way it does.

slebetman
Little correction: args passes not by reference but "by sharing". This is very similar to "by reference" but not a same. Inside a function we receiver _copy_ of reference to passed object. So we can change outer object via `param.prop = 'foo'` but if we do something like this `param = new Object()` it doesn't hurt outer object at all.
NilColor
Right, the old distinction between "pass by value" and "pass by reference" has become hopelessly muddied as the term "reference" has come to mean something completely different. Back in the day, "pass by reference" meant passing *a reference to a variable* or other memory location, something that only C++ and C# still do, and not by default.
Jason Orendorff
+1  A: 

I believe there's a typo in your code. I think you mean:

function TestParam(p) {
    var p = {b:p.b + 1}; // p.b, not p.a (p.a == undefined)
    alert(p.b);
}

In your example:

var a = { b: 1 }; // "a" is defined in the global scope
TestParam(a);
alert(a.b);  // alerts 1

function TestParam(p) {
    var p = {b:p.a + 1}; // "p" is defined in TestParam()'s scope
    /* But not until after the expression on the right side of the assignment
       has completed--that's why you can use p.a 
    alert(p.a); //alerts 2
}

At first, "p" is passed as a reference to the global variable "a", but then you redefine it in the first line. So, for example:

var a = { b: 1 };
alert(a.b); // Alerts 1
TestParam(a);
alert(a.b); // Alerts 2
TestParam(a);
alert(a.b); // Alerts 3

function TestParam(p) {
    p.b++;
}
inxilpro
Yeah, spotted that, probably in the course of you typing this. Thanks for your help :-)
James Wiseman