JavaScript. Private members (instance variables).

As you know JavaScript has no concept of private members in objects. Objects there are “racks of properties” that anyone can change.

Here is a simple way of making “objects” in JS that have private instance variables. Such variables can be changed only by [public] methods you provide.

Consider following code:

  function CreateMyObject()
  {
    var m_first = 0;
    var m_second = 0;

    var envelope = // our public interface
    {
        getFirst: function() { return m_first; },
        setFirst: function(v) { m_first = v; },
        getSecond: function() { return m_second; },
        setSecond: function(v) { m_second = v; }
    };    
    return envelope;
  }  
  
  var f = CreateMyObject();
  
  var n = f.getFirst(); // calling our methods 

Here we have CreateMyObject() that behaves as a constructor of our object. It returns envelope object that has four methods. Only these methods can update or retrieve state of m_first and m_second variables. There are literally no other ways to access them from anywhere else other than set of methods provided.

Under the hood: In this example each interface method that we’ve created is a closure in fact: such methods have access to environment of outer function (CreateMyObject in this case) so they can access variables in this environment. Making a closure can be a memory expensive operation so avoid their use when you expect many instances of such objects.

11 Replies to “JavaScript. Private members (instance variables).”

  1. The idea is neat. the only issue I can see here is the code can be use “new” keyword to initilize the object. anyway, we can claim we are using factory pattern here.

  2. In original article we indeed have ‘no use’ of ‘new’ :

    var f = CreateMyObject();
    var n = f.getFirst(); // calling our methods
    

    In fact above we (almost) have what is today called ‘javascript namespaces’. Which are absolutely important for any serious javascript app. This important implementational JS pattern may be described as:

    function name_space () {
       this.O1 = { /* object 1 */ }
       this.O2 = { /* object 2 */ }
       return this ;
    }
    
    var my = name_space() ;
    my.O1.method() ;
    my.O2.another() ;
    

    This way we have a familiar and very important mechanism of paritioning large(r) javascript code. Also this idiom is more memory efficient than standard object made with ‘new’ keyword.

    Regards: Dusan

  3. My previous comment was somewhat too quick. Here is the complete explanation with javascript specific caveats.
    JS Namespaces are (of course) nothing like namespaces which are part of other languages. In JS case this is just an implementational pattern which helps (but not completely) to do necessary paritioning of the larger JS apps. JS namespaces are only JS objects.
    Here is a little javascript which might explain clrealy the pros and cons of JS namespace.

    function namespace ()
    {
        // scope and visibility of 'counter' and 'F' are private to 'namespace'
        var counter = 0 ; 
        function F(n) { var sign = n < 0 ? "-":"+" ; n = Math.abs(n); "+" ; return sign + (n < 10 ? '0' + n : n) ; } 
        // O, O.m and O.counter all have the public visibility
        this.O = { 
              m : function (x) { return x + "namespace counter : " + F(counter++) + "tO counter: " + F(this.counter++) ; }
           ,  counter : -9  // scope of this is 'this.O'
        }
        return this ;
    }
    
    n1 = namespace() ;
    n2 = namespace() ;
    // n1 and n2 now share the same instance of namespace and also all the instances made inside it !
    s = "" ;
    for ( var j in [0,1,2,3,4] )
    {
      s += n1.O.m("nn1t") ;
      s += n2.O.m("nn2t") ;
    }
    n1 = new namespace() ;
    n2 = new namespace() ;
    // n1 and n2 now DO NOT share the same instance of namespace and also NONE OF 
    // instances made inside it ! 
    // If you run the for() loop above again the values shown will be diferent.
    
  4. Pay attention that when you do call:

    n1 = namespace();
    

    then this environment variable inside the function is a global namespace object.
    So second call:

    n2 = namespace();
    

    will override ‘O’ and will return the same global object – default namespace.

    Thus

      s += n1.O.m("nn1t") ;
    

    is just

      s += O.m("nn1t") ;
    

    And yet each call of namespace() will create its own copy of var counter (in call frame).

  5. Andrew,
    Thank you for your remark. The code shows that I know when n1 and n2 will share the same instnce of namespace() and when they will not. Please see the last portion in the javascript in my comment:

    n1 = new namespace() ;
    n2 = new namespace() ;
    // n1 and n2 now DO NOT share the same instance of namespace and also NONE OF
    // instances made inside it !
    // If you run the for() loop above again the values shown will be diferent.
    

    Also please run the code. And optionally follow it through a debugger.
    Each call of namespace() will NOT create its own copy of var counter (in a call frame).
    First for() loop will output values which will prove that var counter is shared. Same as O.counter. Sometimes there are situations when this is actually a desired behaviour.
    But each “nX = new namespace()” call will indeed create a new set of objects clustered ‘under’ the namespace. So nothing will be shared between n1 and n2, in that case.
    My aim was to show an important javascript idiom, which has to be used in any sizeable javascript app.
    Here I think we see javascript being “easy” still requires a lot of discipline in its actual use.

    Regards: Dusan

  6. I have no doubts that you understand what you are doing, but try this:

        function namespace ()
        {
            // scope and visibility of 'counter' and 'F' are private to 'namespace'
            var counter = 0 ;
            function F(n) { var sign = n < 0 ? "-":"+" ; n = Math.abs(n); "+" ; return sign + (n < 10 ? '0' + n : n) ; }
            // O, O.m and O.counter all have the public visibility
            this.O = {
                  m : function (x) { return x + "namespace counter : " + F(counter++) + "tO counter: " + F(this.counter++) ; }
               ,  counter : -9  // scope of this is 'this.O'
            }
            return this ;
        }
    
        n1 = namespace() ;
        n1O = n1.O;
        n2 = namespace() ;
        n2O = n2.O;
        s = "" ;
        for ( var j in [0,1,2,3,4] )
        {
          s += n1O.m("nn1t") ;
          s += n2O.m("nn2t") ;
        }
        function onClick()
        {
          alert(s);
          alert(n1 === self);
          alert(n2 === self);  
          // thus following will evaluate to true:
          alert(n1.O === n2.O);
          // but this to false:
          alert(n1O === n2O);
        }
    

    This shows that each call of the namespace() 1) creates its own copy of inner object(this.O) and 2) creates its own copy of the counter variable.
    But each call of the namespace() returns the same instance of global namespace object - in browser context it is known as 'self' or 'window' (bad name, imo).
    ()

  7. There are several ways to skin this javascript cat, too ;o) And to make things even less exact it is largely a matter of taste. Technically all these “namespace” solutions work. For example, consider this:

    var my_namespace = {
                       o1 : {}, o2 : {}, o3 : {}
                    }

    Can it be simpler that this? Hardly. Is this “dirty” or “tricky” ? Maybe. But it still works equally well. Do you like it? Do I like it? Maybe, but does it matter? This is javascript: flexible and productive.

    Greetings: Dusan

  8. Yeah, but we’ve returned back to starting point.
    Your my_namespace.o1 is not protected. By writing my_namespace.o1 = null; you can silently ruin your namespace.

    You should have some way to establish controllable perimeter on it. And original envelope does just that.

    If that matters, in tiscript I have added ‘type’ keyword that allows to define such namespaces like:

          type MyNamespace
          {
            type o1
            {
              const foo = 2;
              property bar(v) { get return "bar"; set throw "I am read only!"; }
            }
          }
    

    So you can access it as:

    var v1 = MyNamespace.o1.foo; // v1 will get 2
    var v2 = MyNamespace.o1.bar; // v2 will get "bar"
    

    But these will generate errors:

    MyNamespace.o1 = {}; // runtime error: value of 'o1' is read only. 
    MyNamespace.o1.foo = 8; // runtime error: value of 'foo' is read only. 
    MyNamespace.o1.bar = 12; // error thrown: "I am read only!". 
    

    ‘type’ in tiscipt is an ordinary ‘object’ with all members read only by default.

  9. var f = CreateMyObject();
    
    var n = f.getFirst(); // calling our methods
    
    (function() {
     var parasite='notFirst';
     CreateMyObject.prototype.getFirst = f.getFirst = function(){ return parasite; }
    })
    
    var o = f.getFirst(); // oops! not first!
  10. To Steven: I think you did not get the point of this article.

    Yes, you can write something like this:

    f.getFirst = function(){ return parasite; }

    and so to override/replace getFirst method for this object instance.

    But there is no way in JS to get access to m_first and m_second member variables
    by other methods. Only get/setFirst() and get/setSecond() functions can see and modify them. And that is the point of the article.

Comments are closed.