Wednesday, January 21, 2009

Object-Oriented JavaScript, how to achieve public properties/fields

Recently I posted my findings about private fields in JavaScript. So this is a continuation of the post and it talks about public fields in your JavaScript code. So here is a quick example of public properties in your code:

function User() {
  // Private property
  var name = '';

  return {
    // Public property
    classVersion: '1.3',
    prevVersions: ['1.2.3', '1.2', '1'],

    setName: function(newName) {
      name = newName;
    },
    getName: function() {
      return name;
    }
  };
}
var user = new User();
user.classVersion; // 1.3
user.prevVersions; // ['1.2.3', '1.2', '1']

NOTE:
Define an object property name in your return statement and it will be accessible from outside. In other words - public field.

Public and private methods in JavaScript

I have been talking about public and private properties so far. I guess it is time for private and public methods. The idea behind is the same. To make a method public you need to define it in your return object and if you want to make it private you should declare it outside your return.

Basically:

function User() {
  // Private variable
  var name;

  // Private method
  var privateMethod = function(){
    // Access to private fields
    name += " Changed";
  };

  return {
    // Public methods
    setName: function(newName) {
      name = newName;
      privateMethod();
    },
    getName: function() {
      return name;
    }
  };
}
var user = new User();
user.setName("My Name");
user.getName(); // My Name Changed

As you can see, privateMethod and name are declared outside the return object and thus they are made private. Variables declared inside the return object are public and accessible using dot notation.

7 comments:

  1. Don't you realize how wrong your approach is? Fine, you have private and public methods. But you also have totally broken objects! The methods are created during instantiation time instead of class loading time. That's really really bad. If you create 1000 instances of your User class which has three methods then you have a total of 3000 methods in memory.

    Try it out:

    var user1 = new User();
    var user2 = new User();

    alert(user1.setName === user2.setName);

    You see? The methods are not the same as it should be. If you use JavaScript as it was meant to be used (using prototypes) then it doesn't matter how many instances you create, you always have just the three methods in memory and the test above outputs "true".

    You simply have to deal with the fact that JavaScript doesn't know the concept of private members. But please stop teaching others a totally wrong way to do object oriented programming in JavaScript!

    ReplyDelete
  2. The following syntax will work just as well. Basically, everything refered to with "this" is public, the rest is private.

    function User() {

    // Private property
    var name = '';

    //Public members
    this.classVersion: '1.3';
    this.prevVersions: ['1.2.3', '1.2', '1'];

    //Public methods
    this.setName: function(newName) {
    name = newName;
    };

    this.getName: function() {
    return name;
    };

    };

    var user = new User();
    user.classVersion; // 1.3
    user.prevVersions; // ['1.2.3', '1.2', '1']

    ReplyDelete
  3. @Klaus Reimer, yes, I can't agree more with you. I, myself, was arguing with one of my colleagues that JavaScript is not object-oriented programming language and that it was wrong to compare it to other languages on that basis.

    Let's face it, you are not going to create 1000 User object instances in your web app. Event if you do still they are created on client's PC and we can be sure that it can handle not only 1000 objects.

    Anyway, that's not my point. The point is that there are JavaScript interpreters like Google's V8 JavaScript Engine (http://code.google.com/p/v8/) and I think having private fields and methods would probably be nice.

    This is my opinion and I am by no means propagating javascript as a OO language.

    @Mohamed Attahri, Thanks for sharing...

    ReplyDelete
  4. Here's a bonus to the pattern of public properties:

    function User() {
    function privFunc(){publicAPI.pubProp++;}

    var publicAPI = {
    pubFunc:function(){privFunc();},
    pubProp:1
    };
    return publicAPI;
    }

    var u1 = User();
    u1.pubFunc();
    alert(u1.pubProp); // 2

    u1.pubProp = 10; // change it, obj internally can see change!
    u1.pubFunc();
    alert(u1.pubProp); // 11

    ---------
    Basically, by storing "publicAPI" as a reference to the object returned from the function/object instantiation, you can read/write the values from both outside the object and inside the object.

    ReplyDelete
  5. Check out Douglas Crockford:
    http://javascript.crockford.com/private.html

    He explains very well how Javascript is OOP but the style is so different from what we are use to that we often don't recognize it as OOP.

    ReplyDelete
  6. What about something like this:

    function User() {

    // Private property
    var name = '';

    //Public members
    this.classVersion = '1.3';
    this.prevVersions = ['1.2.3', '1.2', '1'];

    //Public methods
    this.prototype.setName = function(newName) {
    name = newName;
    };

    this.prototype.getName = function() {
    return name;
    };

    };


    Would this actually work? If I am correct, you would have public functions that can access private properties, but the functions would not be copied 1000 times if you have 1000 instances because the functions are stored on the prototype. I think it is basically what Douglas Crockford does in his exploration of inheritance (look at the Sugar section):
    http://javascript.crockford.com/inheritance.html

    ReplyDelete
  7. Not a big fan of this pattern, however Object literals work much better IMHO

    ReplyDelete