First let's go over some fine points of JavaScript, then we can deal with your example.
Function's context
One point of misunderstanding is a context. Every function is called in a context, which is available using a keyword this
. Let's write a function we can use to inspect contexts:
var probe = function(){
// if the context doesn't have a name, let's name it
if(!this.name){
this.name = "lumberjack";
}
// print the name of my context
console.log(this.name);
};
Here we go:
name = "global!";
// when we call a function normally it still have a context:
// the global context
probe(); // prints: global!
var ctx = {name: "ctx"};
// we can set a context explicitly using call()
probe.call(ctx); // prints: ctx
// we can set a context explicitly using apply()
probe.apply(ctx); // prints: ctx
// it is set implicitly, if we call a function as a member
ctx.fun = probe;
ctx.fun(); // prints: ctx
// or we can create a brand new object and set it as a context:
// that's what "new" does
var t = new probe(); // prints: lumberjack
// let's sum it up:
console.log(name); // prints: global!
console.log(ctx.name); // prints: ctx
console.log(t.name); // prints: lumberjack
That's why it is so easy to mess up and fall down inadvertently to the global context.
Returning value in constructor
Many people are confused when they see a constructor returning a value. It is legal. Constructor can return an object, a function, or an array. This value is going to be used as an instance. The old instance is going to be discarded.
var myClass = function(){
// if it is called as a constructor, "this" will be a new instance
// let's fill it up:
this.a = 42;
this.b = "Ford";
this.c = function(){ return "Perfect"; };
// done? let's discard it completely!
// and now for something completely different...
return {
owner: "Monty Python",
establishment: "Flying Circus"
};
};
var t = new myClass();
alert(t.owner + "'s " + t.establishment);
As expected it shows "Monty Python's Flying Circus".
If a constructor returns something else (e.g., a number, a string, the null, the undefined) the returned result is going to be discarded and the old instance will be used.
The example
Your example is hard to understand mostly because of the way it was written. Let's simplify it by rewriting.
First let's deal with the x
:
var x = function() {
this.foo = "foo";
return function() {
this.bar = "bar";
return foo + bar;
};
}(); // returns inner
As we can see the anonymous function (the 1st function
) is executed immediately, so we can inline it:
// next assignment can be simplified because
// top "this" is window or the global scope
//this.foo = "foo"; =>
foo = "foo";
x = function() {
this.bar = "bar"; // this line depends on its context, or "this"
return foo + bar; // this line uses global "foo" and "bar"
};
So at the end we have two global variables: foo
(a string) and x
(a function).
Now let's go over the 1st alert:
alert(x()); // 'foobar', so both 'this' variables are set
Again, let's inline x()
:
// next assignment can be simplified because
// top "this" is window or the global scope
//this.bar = "bar"; =>
bar = "bar";
// at this moment both global "foo" and "bar" are set
alert(foo + bar); // => "foo" + "bar" => "foobar"
The 2nd alert is equally simple:
alert(x.bar); // undefined - but wasn't it used correctly?
It doesn't need much rewriting. x
is a function, we didn't add any properties to it, so x.bar
is undefined. If you add it, you can see results:
x.bar = "bar2";
alert(x.bar); // bar2
The 3rd alert demonstrates JavaScript's OOP in action:
alert(new x().bar); // ok, works
(A side note: it works only because you ran x()
first, otherwise it blows up because bar
is undefined).
Let's rewrite it like that:
var t = new x();
alert(t.bar); // bar
Now let's analyze the constructor. It has two statements: an assignment, and a return. The latter is ignored because it returns a string. So we can rewrite it like that:
x = function(){
this.bar = "bar";
};
var t = new x();
alert(t.bar); // bar
I hope it all looks easy now.