This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this
refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this
refers to the window
object. This is why you get 2
logged twice, because the second time this runs, this.getVal
overwrites the getVal
method that was defined when you ran var a = testFn(4);
.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn
function, not in the global scope. You can run a function with a specific context using the call
method. I also recorded a screencast on call
and apply
which discusses this in greater detail. The basic usage of call
is:
function foo() {...}.call(this);
That executes foo
in the context of this
. So, the first step is to make sure your inner function is called in the right context, the context of the testFn
method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call
is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal
to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn
, you do so in a new scope, so you're not overwriting this.getVal
when you call testFn
for the second time. You can do this using the new
keyword. This SO post on the new
keyword is well worth reading. When you do var foo = new testFn()
you create and execute a new instance of testFN
, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a
and b
to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal());
will give 2
, 4
as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x
globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.