Your function declares two types of constructs. var a is a normal variable that is scoped to the function and it is private within the function. It can be used any way the function needs to, so returning it is no problem at all. Most importantly, its value will not change from one instance of Foo to the next.
b, f1 and f2 aren't being declared as variables. They are being declared as "instance properties", meaning that their data will change from one instance of the object to another. If you want to return an instance property value, you must use this to return the value associated with that particular instance.
After all, if you wrote:
var obj1 = new Foo();
obj1.b = 10;
var obj2 = new Foo();
obj1.b = 20;
How would you be able to keep the two b values separate from each other? The answer is that you have two instances of a Foo object and the obj1 and obj2 variables each store a reference to their own instance.
When you write obj1.b, you need access to the b property that belongs to the obj1 object instance. The this keyword does this for you.
Read on for more details:
The this object binding is volatile in JavaScript...that is, it doesn't always point to the same object and its binding can change from one line of code to the very next. How you invoke the code that contains the word this determines what object it will bind to.
Here's a checklist that you can follow to know what this will bind to...
If the code that contains this is invoked:
As a method or property of an object instance (through an instance variable):
var o = new Object();
// "this" will be bound to the "o" object instance
// while "someProperty" and "someMethod" code executes
o.someProperty = someValue;
o.someMethod();
Via a .call(), .apply(), .bind() or Array.prototype.fn invocation:
// "this" will be bound to the object suppled as the "thisObjectBinding"
someFunction.call(thisObjectBinding, arg, arg);
someFunction.apply(thisObjectBinding, [arg, arg]);
var newFunc = someFunction.bind(thisObjectBinding, arg, arg);
Additionally, several Array.prototype methods allow for a thisObject to be passed which will alter the binding for the duration of the method call:
Array.prototype.every( callbackfn [ , thisArg ] )
Array.prototype.some( callbackfn [ , thisArg ] )
Array.prototype.forEach( callbackfn [ , thisArg ] )
Array.prototype.map( callbackfn [ , thisArg ] )
Array.prototype.filter( callbackfn [ , thisArg ] )
If none of the other scenarios apply, Default binding occurs.
3a. With "use strict" in effect: this is undefined
3b. Without "use strict" in effect: this binds to the Global object
** NOTE: this binding can also be affected by using eval(), but as a general best practice, the use of eval() should be avoided.