9

Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).

function Example() {
    function privateFunction() {
       // "this" is window when called.
       console.log(this);
    }

    this.publicFunction = function() {
       privateFunction();
    }
}

ex = new Example;
ex.publicFunction();

Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?

jthomas
  • 820
  • 6
  • 18
  • 2
    A little trick for managing `this` scope can be found here: http://stackoverflow.com/q/4886632 – Robert Harvey Apr 26 '13 at 00:14
  • 1
    Another solution: [`Function.bind`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind) – Matt Ball Apr 26 '13 at 00:16
  • Unrelated to the `this` question, can't you remove the immediately-invoked anonymous function wrapper from around the inner `function Example()`? It seems to me you'd get the same result if you just declared `function Example()` directly. http://jsfiddle.net/gY4mh/1/ – nnnnnn Apr 26 '13 at 00:44
  • Indeed, it could be unwraped. The example started out as Coffeescript. – jthomas Apr 26 '13 at 01:38
  • Hard to pick a single correct answer. Lots of good information provided. – jthomas Apr 26 '13 at 01:57

8 Answers8

8

Using closure. Basically any variable declared in function, remains available to functions inside that function :

var Example = (function() {
    function Example() {
        var self = this; // variable in function Example
        function privateFunction() {                  
            // The variable self is available to this function even after Example returns. 
            console.log(self);
        }

        self.publicFunction = function() {
            privateFunction();
        }
    }

    return Example;
})();

ex = new Example;
ex.publicFunction();
basarat
  • 207,493
  • 46
  • 386
  • 462
3

Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.

function Test() {
    this.name = 'test';
    this.logName = function() {
        console.log(this.name);
    }
}

var foo = {name: 'foo'};

var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo

Yet another approach is to use "call":

function Test() {
    this.name = 'test';
    this.logName = function() {
        console.log(this.name);
    }
}

var foo = {name: 'foo'};

var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo

both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.

nicksweet
  • 3,279
  • 1
  • 17
  • 20
3

It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:

  1. If you call a function via an object property as in object.method(), then this will be set to the object inside the method.

  2. If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.

  3. If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.

  4. If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.

  5. If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).

  6. In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.

There's a nice summary of these various methods here on MDN.

So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.

If you want to explictly set it to the current value of this in your method, then you can do so like this:

var Example = (function() {
    function Example() {
        function privateFunction() {
            // "this" is window when called.
            console.log(this);
        }

        this.publicFunction = function() {
            privateFunction.call(this);
        }
    }

    return Example;
})();

ex = new Example;
ex.publicFunction();

Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
  • I would argue that in the OP's case it's _better_ to use a closure containing an alias to `this` (per the accepted answer), since internally that's all that `.bind` does and it avoids having to remember to use `.call` syntax instead of normal function call syntax. – Alnitak Jul 01 '15 at 07:09
2

I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable

function Example() {
    var that = this;
    // ...
    function privateFunction() {
        console.log(that);
    }

    this.publicFunction = function() {
       privateFunction();
    }
}

a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.

var privateFunction = function() {
    console.log(this);
}.bind(this);
jAndy
  • 212,463
  • 51
  • 293
  • 348
  • @ErikE the problem was the invocation on a `function declaration`, which is problematic. I modified the code into a `function expression` – jAndy Apr 26 '13 at 00:22
1

I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:

var Example = function(){
  this.prop = 'whatever';
}

Example.prototype.fn_1 = function(){
  console.log(this.prop); 
  return this
}

Example.prototype.fn_2 = function(){
  this.prop = 'not whatever';  
  return this
}

var e = new Example();

e.fn_1() //whatever
e.fn_2().fn_1() //not whatever

Here's a fiddle http://jsfiddle.net/BFm2V/

hobberwickey
  • 5,274
  • 1
  • 24
  • 28
0

If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.

Esteban Araya
  • 27,658
  • 22
  • 99
  • 139
0

In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:

function boundFn(thisobj, fn) {
   return function() {
      fn.apply(thisobj, arguments);
   };
}

Then use it like this:

var Example = (function() {
    function Example() {
        var privateFunction = boundFn(this, function() {
            // "this" inside here is the same "this" that was passed to boundFn.
            console.log(this);
        });

        this.publicFunction = function() {
            privateFunction();
        }
    }

    return Example;
}()); // I prefer this order of parentheses

Voilà -- this is magically the outer context's this instead of the inner one!

You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):

if (!Function.prototype.bind) {
   Function.prototype.bind = function (thisobj) {
      var that = this;
      return function() {
         that.apply(thisobj, arguments);
      };
   }:
}

Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.

ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.

ErikE
  • 43,574
  • 19
  • 137
  • 181
0

I would say assigning self to this is a common technique:

function Example() {
    var self = this;

    function privateFunction() {
        console.log(self);
    }

    self.publicFunction = function() {
        privateFunction();
    };
}

Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.

It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.

Dan Tao
  • 119,009
  • 50
  • 280
  • 431