Private Variables In JavaScript

Often times, many people have made claims that it is not possible to make variables private in JavaScript. I am here to tell you that those people are wrong. There are many implementations from JavaScript guru’s such as Douglas Crockford explaining how to do this. Although these implementations will get the job done, I have noticed that these implementations do not use prototypal functions. On the contrary, the following shows how to give prototypal functions access to private members:

// Immediately executed closure to define a person.
var Person = (function() {
  // The parameter required to retrieve the private data of a person object.
  var theKey = {};
  
  function Person(firstName, lastName, isMale, birthDate) {
    // If birth date is a string, convert it to a date.
    if(typeof birthDate == "string")
      birthDate = new Date(birthDate);
    if(isNaN(birthDate.getTime()) || !(birthDate instanceof Date))
       throw new Error("the passed birth date was invalid");
    // Object literal to keep track of the private data.
    var privateData = {
      firstName : firstName + "",
      lastName : lastName + "",
      isMale : !!isMale,
      birthDate : birthDate
    };
    // If theKey is passed as aKey, the private members of the object are
    // returned.  Since aKey must be theKey in order to retrieve privateData and
    // theKey is an empty object literal, you can be sure that private members
    // will not be returned from the function to any code outside of the
    // closure.  The only non-prototypal function.
    this._ = function(aKey) {
      if(aKey == theKey)
        return privateData;
    };
  }
  
  // Alias for the prototype of the Person object.
  var p = Person.prototype;
  
  // Get or set the first name for the person.
  p.firstName = function(newName) {
    // Get the private data members.
    var pData = this._(theKey);
    // If a parameter was passed, assign the new first name.
    if(!arguments.length)
      return pData.firstName;
    // Set the first name.
    pData.firstName = newName;
    // Allow chainable calls.
    return this;
  };
  
  // Get or set the last name for the person.
  p.lastName = function(newName) {
    // Get the private data members.
    var pData = this._(theKey);
    // If a parameter was passed, assign the new last name.
    if(!arguments.length)
      return pData.lastName;
    // Set the last name.
    pData.lastName = newName;
    // Allow chainable calls.
    return this;
  };
  
  // Gets how many years old the person is.
  p.yearsOld = function() {
    var now = new Date;  // current date
    var birth = this._(theKey).birthDate;  // birth date
    var years = now.getFullYear() - birth.getFullYear();  // difference in years
    now = now.getMonth() * 100 + now.getDate();  // now as MMDD as number
    birth = birth.getMonth() * 100 + birth.getDate();  // now as MMDD as number
    return years - (now < birth);  // Return real years old.
  };
  
  // Gets the birth date.  Makes valueOf an alias so that people can be compared
  // by age.
  p.birthDate = p.valueOf = function() {
    return this._(theKey).birthDate;
  };
  
  // Gets the full name.
  p.fullName = function() {
    var pData = this._(theKey);
    return pData.firstName + " " + pData.lastName;
  };
  
  // Get the string representation of this person.
  p.toString = function() {
    var pData = this._(theKey);
    return "I am a " + this.yearsOld() + " year old "
      + (pData.isMale ? "male" : "female") + " named " + pData.firstName + " "
      + pData.lastName + ".";
  };
  
  // Allow the person pseudo-class to exist outside of this closure.
  return Person;
})();

The code above defines a person pseudo-class in JavaScript. There are four members that are private to each instantiated person object: firstName, lastName, isMale, and birthDate. The first name and last name can be retrieved and set by using the public getters/setters firstName() and lastName(). In addition, fullName() is another accessor method. The person’s birth date can only be retrieved by using either birthDate() or valueOf(). In addition, you can find out how many years old the person is by using yearsOld(). Finally, you can print the person object out as a string to find all of the person’s information.

Now it is time for the tests:

var me = new Person("Chris", "West", true, "October 11, 1492");
var jade = new Person("Jade", "Williams", false, "April 28, 1984");
var harry = new Person("Harry", "Finkelstien", true, "June 4, 1987");

// Show my full name.
alert(me.fullName());

// Change Jade's last name and then show it.
alert(jade.lastName("Flintstone").fullName());

// Show how old harry is.
alert(harry.yearsOld());

// Show everything about the three people.
alert(me);
alert(jade);
alert(harry);

// An attempt to retrieve the private members from the outside.
alert("Could " + (me._({}) == undefined ? "not " : "")
  + "access the private members from the outside.");

I think the comments explain most of the test code pretty well. One thing to note is that, just because you pass in an empty object literal doesn’t mean that it is the same one that is needed to retrieve the private members.

In conclusion, as is demonstrated in my definition of the Person pseudo-class, prototypal functions can retrieve private members.

11 thoughts on “Private Variables In JavaScript”

  1. You can get access to privates like so:

    var person = new Person( ... ),
    secretMethod = person._,
    stolenKey;

    person._ = function( aKey ) {
    stolenKey = aKey;
    };

    try { //It will throw an error cos we can't return proper privates right now
    person.firstName();
    }

    catch(e) {

    }
    //Restore the old method
    person._ = secretMethod;

    //Access privates
    console.log( person._( stolenKey ) );

    So it’s not really private and therefore not much benefits over using public methods with _ prefix convention.

    1. You must remember that in any language where you can simply open a cleartext file and modify the code, you can make private members publicly accessible. On the other hand, if you simply create a JavaScript class using the form that I suggested and the code of the class is not modified, those members that you want to be private will remain private. Even Crockford’s example will no longer provide private members if you modify the class so that the private members become public. Therefore, if you use this form or another one such as Crockford’s, do not modify the code so that you can get access to private members. ;)

        1. I think you misunderstand the difference between his code and my code. His code is basically taking the key that is only found inside of the closure of my code. I do agree that any code within the closure can get access to private data members with this solution but that is why you simply don’t write your code that way. The structure that I am proposing is solely to prevent code outside of the closure from having access to private members. In this way, other third-party JavaScript will never have access to private members.

      1. Can you really modify Crockford’s code (or use it from outside) to access private variable? I am looking at the “Privileged” section in Crockford’s page. The only publicly accessible thing is the “service” method of a container object. If you replace it with something else, you lose the scope to the private members. I don’t really see a way that you can tamper that.

        I agree that all those private variables implementations don’t use prototypal functions, but so does yours. The most basic principle of private variable is that they must be within instance scope and only non-prototypal instance methods have access to their scope. In your example, you are simply having an accessor function that does the link for you, which is non-prototypal. The problem of non-prototypal function is that they get created duplicately in every instance creation and that’s where the criticizing comes from. Your example improves it by minimizing the duplication, but still it could be tampered as mentioned by others.

        I think what people are saying not possible is to implement private variables WITHOUT creating duplicating non-prototypal functions.

        1. It is true that I am creating a non-prototypal function for each instance. I suppose this is the cost for trying to allow prototypal functions access to private data. On the other hand, the non-prototypal function is quite small.

          Also, I would like to address this issue of others tampering with the closure to get access to private data. There is no way, short of modifying the JavaScript file that would define the class(es), that outside functions could get access to private data. The private data accessor function will only return data to privileged code which exists within the closure. The only way the code can exist within the closure is if the developer put the code there. Technically the key could be globally or publicly shared by the developer (which is ill-advised). I suppose a hacker could use a Man in the Middle Attack to alter the JS code (in which case any attempt at data hiding becomes useless).

          In the end, I would agree that it is not possible to implement private variables without duplicating non-prototypal functions but as you can see in this example, the non-prototypal function that is duplicated is extremely light-weight.

          1. Again,
            @Esailija did not modified your code. He only showed it’s (as it is) vulnerable to an attack by other script.
            And that’s because your code passes theKey to a public method. A method that, therefore, can be modified by third party code easily, as he showed.
            By modifying this public method, temporarily, one can make your code give away the key.

            So, there IS a way, without “modifying the JavaScript file that would define the class(es), that outside functions could get access to private data”.

    2. For all those who have been keeping up with this, I apologize profusely for saying that Esailija was modifying my code. I definitely did a cursory overview of his example. Although you have effectively shot a hole into my technique, that just means I need to think about it a little more, right? ;) I am glad everyone responded so many times so that I would come to my senses to recheck Esailija original answer. If I find a better solution, you know I will post it. 8)

      THANKS!!!

Leave a Reply

Your email address will not be published. Required fields are marked *


9 + = eighteen

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="">