4

I'm trying to wrap my head around building a custom JavaScript library. I've read a lot about the module pattern, and also read Crockford's articles on private and public members. I know what is an immediately invoked function expression and why we do stuff like

var myLib = (function() {
}())

However, I'm still a little lost in some cases regarding scope and closures in general. The concrete problem I have is:

Why does the following example alert DOMWindow, rather than the myLib object? http://jsfiddle.net/slavo/xNJtW/1/

It would be great if you can explain what "this" refers to in all of the methods in that example and why.

Slavo
  • 15,255
  • 11
  • 47
  • 60
  • 2
    FYI: `this` has nothing to do with variable scope. It has only to do with the calling context of the function. Its value depends on *how* the function is called. –  Mar 07 '12 at 16:20
  • 3
    BTW, you can `"use strict"` in order to make the "this" in this kind of situation be undefined instead of being the window. It can help a lot to have this kind of error show up more explicitly. – hugomg Mar 07 '12 at 18:27

4 Answers4

4

Inside any function declared (anywhere) and invoked as follows this will be window object

function anyFunc(){
    alert(this);  // window object
}

anyFunc();


var anyFunc2 = function(){
    alert(this);  // window object
}

anyFunc2();

If you want to create private functions and access the instance of 'myObject' you can follow either of the following methods

One

module = (function () {

    var privateFunc = function() {
        alert(this);
    }

    var myObject = {
        publicMethod: function() {
            privateFunc.apply(this); // or privateFunc.call(this);
        }
    };

    return myObject;
}());


module.publicMethod();

Two

module = (function () {

    var _this; // proxy variable for instance

    var privateFunc = function() {
        alert(_this);
    }

    var myObject = {
        publicMethod: function() {
            privateFunc();
        }
    };
    _this = myObject;
    return myObject;
}());


module.publicMethod();

These are solutions to your issue. I would recommend using prototype based objects.

EDIT:

You can use the first method.

In fact here myObject is in the same scope as privateFunc and you can directly use it inside the function

 var privateFunc = function() {
     alert(myObject);
 }

The real scenario were you can use a proxy for this is shown below. You can use call also.

Module = function () {

    var _this; // proxy variable for instance

    var privateFunc = function() {
        alert(this + "," + _this);
    }

    this.publicMethod = function() {
        privateFunc(); // alerts [object Window],[object Object]
        privateFunc.call(this); // alerts [object Object],[object Object]
    }

    _this = this;
    return this;
};

var module = new Module();
module.publicMethod();
Diode
  • 24,570
  • 8
  • 40
  • 51
  • Why create `_this`? Inside an object, `this` should refer to that object when called like `obj.foo()` – Sarfraz Mar 07 '12 at 16:27
  • Answer is in your question - "inside object" . Here you can see that `privateFunc` is not inside `myObject`, means not defined as a function of `myObject`. So we have to use a proxy variable. – Diode Mar 07 '12 at 16:33
  • In all functions of `myObject`, `this` will be `myObject` when invoked using dot operator - e.g: `myObject.publicMethod();` – Diode Mar 07 '12 at 16:37
  • Great. I also saw Crockford was using a proxy, he just called it "that". Didn't quite understand the concept. Which method of the two would you recommend personally, and which one is more widely used? – Slavo Mar 07 '12 at 16:45
  • You can also now use arrow functions to achieve the same thing `() => this` – Liam Mar 19 '19 at 13:51
0

You need to explicitly state that myPrivateMethod is a member of myLib:

function MyLib ()
{
    this._myPrivateField = "private";
    this._myPrivateMEthod = function ()
    { 
         alert(this); // Alerts MyLib function;
    }
}

var libObject = new MyLib();

Just remember that without using enclosure techniques, nothing in JavaScript is ever truly private!

A better way to do the above is like so:

function MyLib(instanceName)
{
    this.name = instanceName;
}

MyLib.prototype.myPrivateFunction()
{
    alert(this);
}

To call your method after that:

var libObject = new MyLib();
libObject.myPrivateMethod(); // Alerts info about libObject.
Thomas Thorogood
  • 2,150
  • 3
  • 24
  • 30
-1

The thing to remember about the module pattern is that it runs once and completes. The methods that are still available to be called are the closures. At the time of creating module, "this" refered to the window and was replaced by its value.

Evert
  • 8,161
  • 1
  • 15
  • 17
-2

In your linked fiddle, the "this" keyword is never changed by a "new" keyword or other context change, so it still refers to the global window object.

edit: clarification

jbabey
  • 45,965
  • 12
  • 71
  • 94
  • 2
    *'"this" is never changed by a "new" keyword ... so it refers to the global window object.'* Completely wrong. –  Mar 07 '12 at 16:14
  • @amnotiam can you explain how/why it is wrong? i would like to learn something instead of just being insulted :P – jbabey Mar 07 '12 at 18:01
  • *"the "this" is never changed by a "new" keyword..."* Remove the word "never" and replace it with "always". *"...or other scope change..."* `new` doesn't cause a scope change. It causes a calling context (`this`) change. *"...so it refers to the global window object."* When does `this` refer to the global window object? When using `new`? In that case it will never refer to the global. Other than that, it will depend on how the function is invoked. Sorry if you felt insulted. –  Mar 07 '12 at 18:26
  • 1
    I think I see where the confusion is coming from... I meant that in the fiddle he posted the "this" is still referring to the window object because he did not invoke his function as a constructor (via new). I think we are saying the same thing, I just worded it poorly. – jbabey Mar 07 '12 at 20:21
  • Update your answer, and I'll remove the DV. –  Mar 07 '12 at 20:26