ptf
ptf

Reputation: 3380

"this" keyword in a javascript module

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

Answers (3)

Ja͢ck
Ja͢ck

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

Frizi
Frizi

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;).

What is 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.

How to construct a valid module?

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

Andreas Louv
Andreas Louv

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

Related Questions