5

I have this code (JSFiddle)

var OBJ = function(){
    var privateVar = 23;
    var self = this;

    return {
        thePrivateVar : function() {
          return privateVar;
        },  

        thePrivateVarTimeout : function() {
            setTimeout(function() { alert(self.thePrivateVar()); } , 10);
        }
    }

}();

alert(OBJ.thePrivateVar());

OBJ.thePrivateVarTimeout();

This is an abstraction of a real problem I'm having.

So - I would expect the call to OBJ.thePrivateVarTimeout() to wait 10 and then alert with 23 (which I want it to access through the other exposed method).

However self doesn't seem to be setting correctly. When I am setting self = this it appears that this isn't a reference to the function but a reference to the global object. Why is this?

How can I make the public method thePrivateVarTimeout call the other public method thePrivateVar?

El Ronnoco
  • 11,753
  • 5
  • 38
  • 65
  • 2
    *Why is this?* Because you are calling the function normally (`func()`). In this case, `this` always refers to the global object. If you want it to refer to an empty object, either call it with `new` or assign one: `var self = {};`. – Felix Kling Dec 13 '11 at 12:10
  • @FelixKling Thanks this makes `self` set correctly. I still can't use it to invoke `thePrivateVar` though. I think Raynos's answer is the way I should do it. – El Ronnoco Dec 13 '11 at 12:15

1 Answers1

5
var OBJ = (function(){
    var privateVar = 23;
    var self = {
        thePrivateVar : function() {
          return privateVar;
        },  

        thePrivateVarTimeout : function() {
            setTimeout(function() { alert(self.thePrivateVar); } , 10);
        }
    };

    return self;

}());

this === global || undefined inside an invoked function. In ES5 it's whatever the global environment is, in ES5 strict it is undefined.

More common patterns would involve using the var that = this as a local value in a function

var obj = (function() {
  var obj = {
    property: "foobar",
    timeout: function _timeout() {
      var that = this;
      setTimeout(alertData, 10);

      function alertData() {
        alert(that.property);
      }
    }
  }

  return obj;
}());

or using a .bindAll method

var obj = (function() {
  var obj = {
    alertData: function _alertData() {
      alert(this.property);
    }
    property: "foobar",
    timeout: function _timeout() {
      setTimeout(this.alertData, 10);
    }
  }

  bindAll(obj)

  return obj;
}());


/*
    bindAll binds all methods to have their context set to the object

    @param Object obj - the object to bind methods on
    @param Array methods - optional whitelist of methods to bind

    @return Object - the bound object
*/
function bindAll(obj, whitelist) {
    var keys = Object.keys(obj).filter(stripNonMethods);

    (whitelist || keys).forEach(bindMethod);

    function stripNonMethods(name) {
        return typeof obj[name] === "function";
    }

    function bindMethod(name) {
        obj[name] = obj[name].bind(obj);
    }

    return obj;
}
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • Thanks, I used the first pattern. I don't really need `self` to be a reference to the overall `OBJ` - I just need the public methods to be able to call each other. However Felix addressed the `self` issue too. – El Ronnoco Dec 13 '11 at 12:21
  • @ElRonnoco I personally prefer the `.bindAll` pattern because `that = this` makes my eyes bleed. – Raynos Dec 13 '11 at 12:23
  • 1
    @Raynos in the line `pd.bindAll(obj)` what is `pd` or where does the pd comes from? – Cristiano Fontes Dec 13 '11 at 12:41
  • 2
    @cfontes I inlined the bindAll method. – Raynos Dec 13 '11 at 12:50