First, let's look at some of the intricacies of JavaScript, and then we'll look at your example.
function context
One point of misunderstanding is context. Each function is called in a context accessible with the this keyword. Let's write a function that we can use to check contexts:
var probe = function(){
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 what "new" does var t = new probe(); // prints: lumberjack // let sum it up: console.log(name); // prints: global! console.log(ctx.name); // prints: ctx console.log(t.name); // prints: lumberjack
This is why it is so easy to spoil and inadvertently fall into a global context.
Return value in constructor
Many people are confused when they see a constructor that returns a value. It is legal. A constructor can return an object, function, or array. This value will be used as an instance. The old instance will be discarded.
var myClass = function(){ // if it is called as a constructor, "this" will be a new instance // let fill it up: this.a = 42; this.b = "Ford"; this.c = function(){ return "Perfect"; }; // done? let discard it completely! // and now for something completely different... return { owner: "Monty Python", establishment: "Flying Circus" }; }; var t = new myClass(); alert(t.owner + " " + t.establishment);
As expected, it features Monty Python's Flying Circus.
If the constructor returns something else (for example, a number, a string, zero, undefined), the return result will be discarded, and the old instance will be used.
Example
Your example is hard to understand, mainly because of how it was written. Let it simplify by rewriting.
First, let's look at x :
var x = function() { this.foo = "foo"; return function() { this.bar = "bar"; return foo + bar; }; }();
As we can see, an anonymous function (1 st function ) is executed immediately, so we can embed 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 (string) and x (function).
Now open the warning 1 st :
alert(x()); // 'foobar', so both 'this' variables are set
Again, let the string 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"
Warning 2 nd is equally simple:
alert(x.bar);
No need to rewrite a lot. x is a function, we did not add any properties to it, therefore x.bar is undefined. If you add it, you will see the results:
x.bar = "bar2"; alert(x.bar); // bar2
Warning 3 rd demonstrates the effect of OOP JavaScript:
alert(new x().bar);
(Note: it only works because you first ran x() , otherwise it explodes because bar is undefined).
Rewrite it like this:
var t = new x(); alert(t.bar);
Now let's analyze the constructor. It has two statements: assignment and return. The latter is ignored because it returns a string. Therefore, we can rewrite it like this:
x = function(){ this.bar = "bar"; }; var t = new x(); alert(t.bar);
I hope everything looks simple now.