views:

312

answers:

1

This question is about the behavior of an object with methods added to its prototype chain and a few private variables. Just out of curiosity and to get my head around this riddle.

function SomeObject() {
    if (this instanceof SomeObject) {
      var args  = arguments[0],
          proto = SomeObject.prototype,
          privatevalue = 0,
      /** assign first element of args[0] 
        * (=instance name) to private variable [id],
        * to be able to keep track of the instance
        */
          id = args[0] ||'no id';


      /** public 'set' adds a value to
        * the private variable [privatevalue]
        */
      this.set =
         function(){
           privatevalue += arguments[0] || 1;
           return privatevalue;
          };
       /** toString functions as a kind of getter */
       this.toString = function(){
            return id+' privatevalue: '+privatevalue;
       }

       /** add two methods to the prototype chain 
         * this happens only once
         */
       if (!proto._initialized_) {
         /** SomeObject.prototype.add 
           * uses 'this.set' to add a value
           * to [privatevalue] and returns
           * the object value (from tostring)
           */
         proto.add =
           function(){
             this.set(arguments[0]);
             return this;
         };
         /** SomeObject.prototype.add2 
           * uses 'this.set' to add a value
           * to [privatevalue] but returns
           * a string using private variables
           * [id] and [privatevalue] 
           */
         proto.add2 =
            function(){
              this.set(arguments[0]);
              return id+' privatevalue: '+privatevalue;
         };
         proto._initialized_ = true;
        }

        } else {
           return new SomeObject(Array.prototype.slice.call(arguments));
        }
  }

  /** create 2 instances of SomeObject */
  var objA = SomeObject('objA'),
      objB = SomeObject('objB');

  /** show the values and use the prototype [add] method
    * to change [privatevalue]
    */
  alert ([objA, objB].join(' | ')); 
        //=> objA privatevalue: 0 | objB privatevalue: 0
  alert ([objA.add(4), objB.add(2)].join(' | ')); 
         //=> objA privatevalue: 4 | objB privatevalue: 2

  /** use prototype method 'add2' to change and view the
    * private variables [id] and [privatevalue] for objA
    */
  alert (objA.add2()); 
         //=> objB privatevalue: 2!

Now the question is: why does the prototype method add2 from ojbA (thus: objA.add2()) return the values of the private variables from objB? I would say these privates shouldn't be accessible for objA. In other words: what kind of scoping is going on here? Stranger still. If you do this:

  alert (objA.add2()); 
  alert (objB.add2());

you get for objA.add2(): objA privatevalue: 5 and for objB.add(): objA privatevalue: 5

+1  A: 

The problem is the scope in which you create the add functions.

They are created in the scope of SomeObject execution when you instantiate 'objA'.

Hence id and privatevalue variables accessed by Add2 are the ones belonging to 'objA' regardless of whether you call .Add2 agains 'objA' or 'objB'.

Fundementally prototype based methods cannot to access private members of a class. In order to access private members a function needs to be created in the same scope (or a child scope) of those members. This is why set works because its created on each instantiaion of SomeObject.

AnthonyWJones
Aren't 'add' and 'add2' created in the prototype chain, so let's say in the prototype scope and 'set' and 'toString' in the function scope?
KooiInc
No, there is no "prototype scope". add and add2 are created in the function scope when 'objA' is being created. They are added to the prototype chain so that the instance of 'objB' can see them but they will access the variables from the function scope in which they were created.
AnthonyWJones
All right, I think I understand. 'add2' can access only the private variables that were assigned at first instantiation. The question didn't concern a problem by the way, it was just curiosity about and not completely understanding something I noticed using this constructor creation pattern.
KooiInc
This might help a little more:- http://stackoverflow.com/questions/99927/oo-javascript-definitive-explanation-of-variable-scope/101089#101089For you the important thing is to remember the scope chain and prototype chain are two different things.
AnthonyWJones