Reputation: 3380
I have defined the following module globally:
var module = (function () {
console.log(this);
this.fn = function () {
console.log(this);
}
return this;
})();
http://www.quirksmode.org/js/this.html :
In JavaScript |this| always refers to the “owner” of the function we're executing, or rather, to the object that a function is a method of.
The first call to console.log
logs Window
as the value of this
, and that I understand. But, so does also the second call to console.log
.
Since this
refers to the owner of the function, why does module.fn
log Window
and not module
?
When I call fn
I still have to write module.fn
, I can't write Window.fn
. Since this
refers to Window
I find this confusing.
EDIT: I forgot to return this
in my example.
Upvotes: 0
Views: 248
Reputation: 173662
Since this refers to the owner of the function, why does module.fn log Window and not module?
The return value of the outer function is window
because it doesn't get called in any particular context, so module
ends up being window
as well.
It seems that the way you have applied the module pattern is wrong. It should be returning the public interface that gets used in the rest of your code:
var module = (function () {
console.log(this);
// public interface
return {
fn: function () {
console.log(this);
}
}
})();
module.fn(); // "Object {fn: function}"
Upvotes: 4
Reputation: 2940
In your example, the global object receives the fn
. It is the window
object in case of browsers. That is because you are calling the function in place (effectively constructing a new scope) without specific context.
On the end, your module
object is just a reference to the window
(because of return this;
).
this
?In JavaScript, this
is the current context, the object on which the function was called that particular time. It is not the "holder" of the function. You can always "steal" the method from other objects and apply
(literally) it to your own.
Assume you want to slice the arguments
object for some reason. It looks just like an array, but it is NOT an array. arguments.slice(2,4)
does not work (assuming ECMAScript < 5). What to do?
Array.prototype.slice.apply(arguments, [2,4]);
You need to steal the slice
function from the Array prototype, and use if on your arguments. Inside the slice
call, the "this" is the arguments object that particular time.
Your job is to return
the module object. You do not want do mess with the context. It is not relevant, as long as you are not applying the function directly on module object.
The easiest solution is the simplest.
var module = (function() {
// do something internally, great for "private" stuff
// then return the "public" interface
return {
doSomething: function() {
// do something
},
introduce: function() {
console.log(this);
}
};
})();
module.introduce(); // Object {doSomething: function, introduce: function}
module.doSomething();
The other way.
Alternatively, you could use the this
to do your job, using the apply, if you really want to.
var module = {};
(function(){
this.doSomething = function() {
// do something
};
this.introduce = function() {
console.log(this);
};
}).apply(module);
module.introduce(); // Object {doSomething: function, introduce: function}
module.doSomething();
Note this is almost equal to the "new" call.
There are more equally valid ways to do it, but the first presented one is frequently used and very clear. Anyway, everything really depends on your code conventions.
Upvotes: 2
Reputation: 47127
Your pattern is wrong what you are doing to make a closed scope and setting module
to the return from that scope:
// This is the design pattern:
var module = (function () {
var module = {};
var localVar = 1;
module.moduleVar = 2;
module.fn = function () {
console.log(this);
}
return module;
})();
console.log(module.fn); // function() { console.log(this) ;}
console.log(module.moduleVar); // 2
console.log(module.localVar); // undefined
Upvotes: 2