1

I have a function defined in javascript that acts as a class. Within it, I have several public methods defined, but there is one private method, and I need to access one of the public methods within it.

function myClass(){ 
   this.myPublicMethod = function(a,b){
       var i = a*b;
       return i;
   }

   function myPrivateMethod(){
        var n = this.myPublicMethod(2,3);
   }
}

This doesn't work. Is there a way to access myPublicMethod within myPrivateMethod?

William Oliver
  • 1,412
  • 4
  • 24
  • 35
  • 1
    How is the private method called? – Bergi Oct 08 '13 at 00:16
  • Make sure to have a look at my answer ;) – plalx Oct 08 '13 at 00:22
  • Oh my, we're talking about "private" methods (not instance specific), you can put those on the prototype together with the "privileged" methods that need to call them. Can't believe how wrong the answers here are. If you need instance specific "private" members than you'll have to throw out prototype all together and can use a function to return an object containing closures. The latter is very bad design in my opinion and the first I never used but am aware of it's existence. Current standard is to define "private" methods by their name starting with low dash: `_privateMember` – HMR Oct 08 '13 at 05:42
  • @HMR I would not say "current standard". – kapa Oct 08 '13 at 09:22
  • @bažmegakapa What popular framework or library is not using it right now? – HMR Oct 08 '13 at 10:11
  • 1
    @HMR Now I understand in what sense you use the word "standard" :). – kapa Oct 08 '13 at 10:44
  • @bažmegakapa Since this is uncharted area and ECMA doesn't say you should do it this or that way there actually is no standard. So I guess I meant "it's the way it's done" standard. Looking at closure library I see they use it and refrain from 'privates" (=closures). The compiler takes care of it. I thought typescript don't use privates as well but their playground is broken at the moment: http://www.typescriptlang.org/Playground/ But looks like they don't use closures to create privates. Instead it's the naming convention `_`: https://typescript.codeplex.com/discussions/397651 – HMR Oct 08 '13 at 11:33
  • @HMR Thanks. I'm kind of confused by what you said. Can you put it into context of the code in my question, or something similarly simple? – William Oliver Oct 13 '13 at 04:58
  • @WilliamOliver I've added an answer, if you have a specific question about the code in my answer I'll be happy to walk you through it. Protoype and inheritance/mix-in through prototype could take a while to fully understand. The easy thing is to remember is that instances of your object (instance = new MyObject) share the properties on your construcor's prototype (MyObject.prototype). This is demonstrated in the start of this answer: http://stackoverflow.com/a/16063711/1641941 I usually stay away from simulating privates and use the naming convention (`_myprivate`) – HMR Oct 13 '13 at 05:27

7 Answers7

2

You simply have to specify the value of this when calling your private method using Function.prototype.call.

myPrivateMethod.call(this);

E.g.

function myClass(){ 
   this.myPublicMethod = function(a,b){
       var i = a*b;
       return i;
   }

   function myPrivateMethod(){
        var n = this.myPublicMethod(2,3);
   }

   //calling private method in the *scope* of *this*.
   myPrivateMethod.call(this);
}

Please note that having true private members (that aren't functions) comes at the cost of not taking advantages of prototypes. For that reason, I prefer to rely on naming conventions or documentation to identify private members rather than enforcing true privacy. That holds only for non-singleton objects.

The following example demonstrates what is being said above.

//Constructors starts by an upper-case letter by convention
var MyClass = (function () {

    function MyClass(x) {
        this._x = x; //private by convention
    }

    /*We can enforce true privacy for methods since they can be shared
    among all instances. However note that you could also use the same _convention
    and put it on the prototype. Remember that private members can only be 
    tested through a public method and that it cannot be overriden.*/
    function myPrivateMethod() {
        this.myPublicMethod1();
    }

    MyClass.prototype = {
        constructor: MyClass,
        myPublicMethod1: function () {
            //do something with this._x
        },
        myPublicMethod2: function () {
            /*Call the private method by specifying the *this* value.
            If we do not, *this* will be the *global object* when it will execute.*/
            myPrivateMethod.call(this);            
        }
    };

    return MyClass;

})();
plalx
  • 42,889
  • 6
  • 74
  • 90
  • Not that I ever use it but added the correct module pattern for having privileged methods that can call "private" methods. The OP didn't have instance specific privates so no point in having everything in the constructor body. – HMR Oct 08 '13 at 05:11
  • @HMR, I added a code example that puts some more emphasis on the note I had written. – plalx Oct 08 '13 at 13:43
1

You can try this to defeat the local scoping:

function myClass(){ 

   this.myPublicMethod = function(a,b){
       var i = a*b;
       return i;
   }
   // Capture the original context of `this` myClass
   var self = this;
   function myPrivateMethod(){
        var n = self.myPublicMethod(2,3);
   }
}

We use self to maintain a reference to the original this even as the context is changing (since we want to call the public method int the private method).

Josiah Hester
  • 6,065
  • 1
  • 24
  • 37
  • Not that I ever use it but added the correct module pattern for having privileged methods that can call "private" methods. The OP didn't have instance specific privates so no point in having everything in the constructor body. – HMR Oct 08 '13 at 05:12
1

One way to do this is defining every method as private first, and making the ones you want public in the end (myPrivateMethod will reference the original myPublicMethod even if myClass.myPublicMethod is overridden):

function myClass(){ 
   var myPublicMethod = function(a,b){
       var i = a*b;
       return i;
   }

   var myPrivateMethod = function (){
        var n = myPublicMethod(2,3);
   }

   this.myPublicMethod = myPublicMethod;
}
kapa
  • 77,694
  • 21
  • 158
  • 175
  • Ill probably do this in my next project. – William Oliver Oct 08 '13 at 00:12
  • @WilliamOliver There are lots of different styles available. You should decide whether you need constructors at all... simple object literals could be enough and you can use `Object.create()` for "inheritance". If you want to use constructors, you can also try returning a crafted object from the constructor. – kapa Oct 08 '13 at 00:17
  • 1
    Note that this works only if the private method doesn't rely on `this`. – plalx Oct 08 '13 at 00:20
  • I apologize for my answer, I didn't read your question thoroughly enough. – y-- Oct 08 '13 at 00:33
  • Not that I ever use it but added the correct module pattern for having privileged methods that can call "private" methods. The OP didn't have instance specific privates so no point in having everything in the constructor body. – HMR Oct 08 '13 at 05:14
  • @plalx It does not need to rely on `this` anymore, but what you're saying is right. – kapa Oct 08 '13 at 09:20
0

You could write

var myPublicMethod = this.myPublicMethod = function()...

So there was a private and a public instance created.

Just seems cleaner to me.

Robbie Wxyz
  • 7,671
  • 2
  • 32
  • 47
0

You could potentially pass the public function to the private function as a parameter and then call it.

this.myPublicMethod = function(a,b){
   alert(a * b);
}

function myPrivateMethod(publicMethod){
    var n = publicMethod(2,3);
}
myPrivateMethod(this.myPublicMethod);

Here is a working fiddle.. http://jsfiddle.net/rxtB2/3/

Charlie Martin
  • 8,208
  • 3
  • 35
  • 41
0

An alternative to invoking call could be to attach the method directly to the myClass Function object.

function myClass(){ 

   myClass.myPublicMethod = function(a,b){
       var i = a*b;
       return i;
   }

   this.myPublicMethod = myClass.myPublicMethod;

   function myPrivateMethod(){
        var n = myClass.myPublicMethod(2,3);
        return n;
   }

   console.log("Calling myPrivateMethod()");
   console.log(myPrivateMethod());

}
thgaskell
  • 12,772
  • 5
  • 32
  • 38
  • Not that I ever use it but added the correct module pattern for having privileged methods that can call "private" methods. The OP didn't have instance specific privates so no point in having everything in the constructor body. And yes, if you use the pattern correctly you have to use call or apply because the privileged and private methods are on the prototype thus `this` is unknown at the time of declaring them. – HMR Oct 08 '13 at 05:15
0

If you only want "private" methods then your pattern for defining it is wrong. I think methods that can access "private" methods are called privileged methods but can be wrong.

The pattern for creating them is the following (added instance variables and inheritance in the example):

// added to show inheritance
var Parent = function(){
  //instance members
  this.parentInstanceVar=["parent"];
};
var Class = function() {
  //1st step to "inherrit" from Parent
  // take ownership of Parent instance members
  Parent.call(this);
  //to pass all arguments to Parent constructor:
  //Parent.apply(this,arguments);
  //to pass specific agruemtns to Parent
  //Parent.call(this,arg1,arg5);
  this.someIntanceVar=["Class instance"];
};
Class.prototype=(function(parent){
  var ret=(parent&&parent.prototype)?
    Object.create(parent.prototype):
    Object.create({});
  //repair the constructor
  ret.constructor=Class;
  var myPrivateMethod = function() {
    return this.someIntanceVar;
  };
  //privileged method calling "private method"
  ret.myPriviligedMethod=function(){
    return myPrivateMethod.call(this);
  };
  return ret;
}(Parent));//2nd step to "inherit" from Parent
//non privileged method
Class.prototype.nonPrivileged=function(){
  //can't accesss myPrivateMethod here
};

//some tests creating 2 instances
var c1=new Class();
var c2=new Class();
c1.parentInstanceVar.push("added in c1");
c1.someIntanceVar.push("added in c1");
console.log(c2.parentInstanceVar);//=["parent"]
console.log(c2.someIntanceVar);//=["class instance"]
console.log(c1.myPriviligedMethod());//=["Class instance", "added in c1"]
console.log(c2.myPriviligedMethod());//=["Class instance"]
//reason why we repaired the constructor:
console.log((new c1.constructor()) instanceof Class);//=true

This pattern only handles instance shared private members. If you need instance specific private members you can't use prototype for any privileged methods (methods that need access to "private" instance specific members). You can use use a function that returns an object containing closures but I would really not use this pattern as it ignores prototype and the benefits that come with it, makes testing harder and is a huge pain to clone.

var createClass=function(privateInstanceVal){
   var privateMethod=function(val){
     privateInstanceVal.push(val);
     return privateInstanceVal;
   };
   return{
     publicInstanceVar:[],
     publicMethod:function(val){return privateMethod(val);}
   }
};
c1=createClass([]);
var b = c1.publicMethod(33);
console.log("b is:",b);
b.push("because I returned the private I made it public");
console.log(c1.publicMethod(0));//=[33, "because ... public", 0]

The sample shows a mistake sometimes made by returning "private" you made it public. Instead you can return a copy: return privateInstanceVal.concat([]);

HMR
  • 37,593
  • 24
  • 91
  • 160