February 17, 2011

Private prototype methods in JavaScript

JavaScript does not know such a thing as "private methods". But it does know closures. And with this simple yet powerful tool we can implement functions that behave like private methods in an object's prototype with ease.

This is how it will look like:
var Foo = function () {};
Foo.prototype = (function () {

    // Private attributes
    var somePrivateAttribute = 'Hello world';

    // Private methods
    function somePrivateMethod(val) {
        alert(val);
    }

    // Public attributes and methods
    return {
        somePublicMethod: function () {
            somePrivateMethod(somePrivateAttribute);
        }
    };
})();

The "magic" here is the immediate function invocation. This function only returns those methods that should be public available in the prototype and holds the "private" stuff in the scope of the so created closure.

That way the "public" prototype functions have full access to the "private" members. But it is not possible to access the "private" members from anywhere besides those functions returned by the immediate function invocation.

Pretty nice stuff, isn't it? :)

To summarize this pattern we can say:
  1. Wrap the prototype members in an immediately invoked function
  2. Return those members that should be public available to the prototype
  3. Declare private attributes and methods inside the immediately invoked function but do not return them.

15 comments:

  1. Pattern that you described above is modular pattern.
    good job

    ReplyDelete
  2. While the article is valid, the title is misleading. This is not a private prototype method. It's "private" yes, but it's not a prototype method. It's a function defined in a closure that also closes over the definition of a set of prototype methods. You can do the same thing by changing it around a bit:

    var Foo = function() {};
    (function() {
    var somePrivateFunction = function() { ... };
    Foo.prototype = { somePublicMethod: function() { ... } };
    }());

    but it's not a private prototype method.

    ReplyDelete
  3. @Justin: but he's assigning something to the "class" prototype: Foo.prototype = xxxxxx
    So isn't that an example of prototype usage? I'd like to understand this better.

    ReplyDelete
  4. Good post. Don't forget to use call() on the returned object to give you access to 'this', otherwise 'this' will be window:

    function Foo ( colour, name ) {
    this.colour = colour;
    this.name = name;
    };
    Foo.prototype = (function () {

    // Private attributes
    var somePrivateAttribute = 'Hello ';

    // Private methods
    function somePrivateMethod(val) {
    console.log(val + this.name + ' is your favourite colour really ' + this.colour + '?');
    }

    // Public attributes and methods
    return {
    somePublicMethod: function(){
    somePrivateMethod.call(this, somePrivateAttribute);
    }
    };
    })();

    kungFoo = new Foo('blue','Bruce Lee');
    kungFoo.somePublicMethod();

    ReplyDelete
  5. Good patern, but because "somePrivateAttribute" is defined in the prototype, it is shared with all instances of "Foo" (like a static field in C#).
    Is there a solution to this problem in javascript?
    (Sorry for bad English, if any. i'm trying to learn english... :)

    ReplyDelete
    Replies
    1. Unfortunately, no. Imagine a prototype is a list of static definitions of a class, they're only put in memory once as everything is static. The only solution would be to have an array of private variables then with EVERY call you would have to reference which one you were refering to... Which would be a very good way to implement errors into your code that would be VERY hard to find. So don't do that.

      The only GOOD solution is one which doesn't fully satisfy your want for it to have fully private variables, you can instead have variables WITH get/set methods which your prototypes use to access private methods within your function's closure. Atleast this way you can control how the data is set and run condition checks to make sure that all data entered is valid.

      I think this is a valid solution as one of the best things about private variables is that you control how data is entered. Getter/Setter methods is pretty much the same thing, but not 100% control.

      Hope this helps

      Delete
    2. Actually, I have been able to make a solution that works, just define an id which is set during the construction of Foo, and have an accessor method which returns the id, then inside of the prototype you have a private array of values, which are accessable via the ID inside of the Foo class through the accessor method.

      see the code at: http://pastebin.com/MKYjS8rR
      or below, the link though is formatted

      var Foo = function (newID)
      {
      var id = newID;

      this.getId = function() { return id; };
      };
      Foo.prototype = (function () {

      // Private attributes
      var somePrivateAttribute = {};

      // Private methods
      function somePrivateMethod(val) {
      alert(val);
      }

      // Public attributes and methods
      return {
      somePublicMethod: function () {
      somePrivateMethod(somePrivateAttribute);
      },
      setPriv: function(newVal) {
      somePrivateAttribute[this.getId()] = newVal;
      },
      getPriv: function() {
      return(somePrivateAttribute[this.getId()]);
      }
      };
      })();

      var a = new Foo(0);
      var b = new Foo(1);
      var c = new Foo(2);

      a.setPriv("a");
      b.setPriv("b");
      c.setPriv("c");

      console.log(a.getPriv());
      console.log(b.getPriv());
      console.log(c.getPriv());

      Delete
    3. btw heres some code you can use to see it in work, I use JS Bin to test:


      var a = new Foo(0);
      var b = new Foo(1);
      var c = new Foo(2);

      a.setPriv("a");
      b.setPriv("b");
      c.setPriv("c");

      console.log(a.getPriv());
      console.log(b.getPriv());
      console.log(c.getPriv());

      Delete
    4. Or you could just define the non private data as var's inside of the "class" (function) you plan on prototying, then add getter/setter methods for the data.. :P personally I prefer this as it is better practice

      Delete
    5. Oh and if your main reasoning behind wanting the data/methods to be private, maybe consider using the Revealing Prototype Structure, heres a good explanation: http://weblogs.asp.net/dwahlin/archive/2011/08/03/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-prototype-pattern.aspx

      Basically, its a prototype patern with a revealing module inside of the .prototype definition which is self invoking. heres a VERY basic example:

      var Calc = function(OutputDiv) {
      this.output = document.getElementById(OutputDiv);
      };

      Calc.prototype = function() {
      var add = function(x, y) {
      var val = x + y;
      this.output.innerHTML = val;
      };

      return {add: add};
      }();

      Delete
    6. Hmm, just re-read this blog, it seems as if he is using a non-Revealing(Module) Prototype Structure

      Try looking into the Module Structure, the revealing Module Structureand the Prototype Structure.

      Delete
    7. just to clarify, the Revealing Prototype Structure only has private functions, all the variables are public (hens the this. infront of all of them)

      But that shouldn't (well you hope it shouldn't) be an issue, the main issue with the prototype structure is that things as such as intilisense or auto complete will pickup all of the function definitions, uncluding ones that might have to be called in a very specific way... so other people using your code might find it hard to figure out what functions to use.. They might think that they should use a method they shouldnt. The the Revealing Prototype Structure solves this issue..

      Javascript is supposed to be open, having variables public isn't as much of a no no as it is inside of languages as such as the C variant languages. Don't be afraid to leave things as public, it is after all one of the key methodologies of javascript.

      Sometimes though it is smart to make an exception, for example using the Prototype Pattern or the Revealing Prototype Pattern so that data that the user has 0 to gain from having access over is hidden so they don't accidently impliment it incorrectly.

      Delete
  6. If anyone is interested in having private methods on the prototype chain, I have written a solution to your problems and would love to hear your feedback! Check it out here https://github.com/TremayneChrist/ProtectJS There is a basic example you can follow included in the source.

    ReplyDelete