views:

172

answers:

4

I don't understand the following:

var x = function() {
 this.foo="foo";
 return function() {
  this.bar = "bar";
  return foo+bar;
 };
}(); // returns inner

alert(x()); // 'foobar', so both 'this' variables are set
alert(x.bar); // undefined - but wasn't it used correctly?
alert(new x().bar); // ok, works

My assumption was that a default 'this' scope/variable-map is generated and used the first time, and then when 'new' is called, a new object (function?) with a new 'this' is sent through and returned. Or, perhaps x isn't a proper object? But then, how does 'this' end up being set and used to make 'foobar'?

What do I need to know to understand this?

+3  A: 

To answer the question you ask in your title: this will reference the global object.

Keep in mind, this does not behave in JavaScript as it does in languages like Java or C++; it's used purely for context, and will not necessarily reference the same sort of object every time a given function is called. From the MDC documentation:

There are four ways this can be passed
[...]
If none of the above ways are used, the global object is passed as the context object, e.g., when this occurs at top-level outside of any constructor, or when a function is called without being called as a method of an object, as in func(arg1, arg2).

For answers to the rest of your question, please see: Just when I think I finally understand Javascript scope…

Shog9
+3  A: 

As Shog9 said, this is not the same as normal scoping like you see in Java, etc.

AFAIK Javascript uses dynamic scoping, like Common LISP/eLisp does, and not lexical scoping like Scheme/Lisp-1 does.

leppie
+4  A: 

That's the major complain of the new operator...

That operator creates a new object that inherits from the prototype of the operand constructor function, and then it calls the function, assigning the new object to this.

If you forget to use the new operator when calling a constructor function, you get instead normal function call, and this is bound to the global object (window) instead of to a new object.

Your function will be appending global variables whenever it uses this attempting to initialize its own instance.

In your example the global object ends up with two new variables:

window.foo
window.bar

Because of that some people prefer prototypal inheritance instead of the pseudo-classical approach.

CMS
See also: http://stackoverflow.com/questions/383402/is-javascript-s-new-keyword-considered-harmful
Shog9
+5  A: 

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.

Eugene Lazutkin
This is a very helpful tutorial (though the first inline steps are hard to follow without knowing how 'this' works). Thank you!
mk
I rearranged my original post so it flows more logically. Thank you for the suggestion how to make it better!
Eugene Lazutkin