18

Say I have a javascript function/class called Foo and it has a property called bar. I want the value of bar to be supplied when the class is instantiated, e.g:

var myFoo = new Foo(5);

would set myFoo.bar to 5.

If I make bar a public variable, then this works, e.g:

function Foo(bar)
{
    this.bar = bar;
}

But if I want to make it private, e.g:

function Foo(bar)
{
   var bar;
}

Then how would I set the value of the private variable bar such that its available to all internal functions of foo?

Ali
  • 261,656
  • 265
  • 575
  • 769
  • FWIW, you cannot have truly private variables and make use of prototypes. I personally would provide [proper documentation](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml#Visibility__private_and_protected_fields_) instead of making the code more complex... – Felix Kling Jul 23 '11 at 08:38
  • @FelixKing How is that true? – Melab Nov 08 '18 at 00:50

7 Answers7

56

One of the best tutorials on private and protected access in javascript is here: http://javascript.crockford.com/private.html.

function Foo(a) {
    var bar = a;                              // private instance data

    this.getBar = function() {return(bar);}   // methods with access to private variable
    this.setBar = function(a) {bar = a;}
}

var x = new Foo(3);
var y = x.getBar();   // 3
x.setBar(12);
var z = x.bar;        // not allowed (x has no public property named "bar")
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 10
    Whoever downvoted this, can you explain why? This looks correct to me. +1 – Paul Jul 23 '11 at 08:08
  • I didn't, but in many languages it's nice to be able to use the same parameter name instead of something like 'a'. That is, having a parameter named 'bar' and assigning it to a variable named 'bar' – Emmanuel M. Smith Mar 02 '12 at 12:25
  • @MrAxilus - In javascript, you can't have a local variable with the same name as a function argument and have access to both by that name. – jfriend00 Mar 02 '12 at 14:27
  • @PaulP.R.O. This is clearly a -1 because every time you instantiate the `Foo` class, getBar and setBar are also being defined again and it's clearly inefficient. To define methods you have to use the prototype property from outside the constructor (the Foo function). – Gabriel Llamas Mar 29 '12 at 18:37
  • 3
    @GabrielLlamas - I think you don't understand the point of this code. If `bar` is to be truly private so it cannot be accessed from the outside (which is what the OP asked for), then it has to be declared as I've done. And, if you declare `bar` as I've done, then you cannot access it from methods defined on the prototype. You have to define your functions inside the scope of `bar`. Yes, it isn't quite as efficient if you're instantiating lots of `Foo()` objects, but it IS the way to make bar private which was the question being asked. Please remove your downvote. – jfriend00 Mar 29 '12 at 21:37
  • @jfriend00 Sorry, I can't accept a solution that promotes bad practices. The accpeted answer should be "Don't try to hack JavaScript, truly private variables don't exist". As you've said, you're loosing the benefits that prototype gives you. – Gabriel Llamas Mar 30 '12 at 07:37
  • 7
    @GabrielLlamas - there's no bad practice with this approach. It is simply a tradeoff. In order to achieve privacy, you accept a tiny performance hit when the object is created. Once created the object performs perfectly fine. The prototype is there as a convenience. There's no reason you have to use it to achieve your goal. If you were creating lots of `Foo()` objects and performance was paramount, this would be a bad tradeoff. But, if you're only creating a couple or the performance of this method was perfectly fine AND you wanted variable privacy, this method is a good practice. – jfriend00 Mar 30 '12 at 11:48
  • 2
    @GabrielLlamas - This method of obtaining privacy is not a hack. It uses a supported feature of the language (a closure) to solve a problem not originally designed into the language. That isn't a hack - it's a creative solution that there is absolutely nothing wrong with using if the capabilities it provides are desired. – jfriend00 Mar 30 '12 at 11:55
24

You have to put all functions that need to access the private variable inside the constructor:

function Foo(bar)
{
  //bar is inside a closure now, only these functions can access it
  this.setBar = function() {bar = 5;}
  this.getBar = function() {return bar;}
  //Other functions
}

var myFoo = new Foo(5);
myFoo.bar;      //Undefined, cannot access variable closure
myFoo.getBar(); //Works, returns 5
Digital Plane
  • 37,354
  • 7
  • 57
  • 59
4

One way I can think of is to use a closure that's assigned to a name and returns a new object. You would pass in any arguments to the constructor through the call to the closure. That would end up being something like the following:

var fooFactory = function (a, b) {
    var c = 5,
        d = 6,
        foo;

    foo = function (a, b) {
        this.a = a;
        this.b = b;
        this.bar();
    }

    foo.prototype.bar = function () {
        //do something with c and d
        this.c = c + d;
    }

    foo.prototype.getC = function () {
        return c;
    }

    foo.prototype.getD = function () {
        return d;
    }

    return new foo(a, b);
};

This way, a and b are always declared uniquely. You would then construct your object like so:

var obj = fooFactory(1, 2);
//obj contains new object: { a: 1, b: 2, c: 11 }

console.log(obj.getC());
//returns 5
marksyzm
  • 5,281
  • 2
  • 29
  • 27
  • I voted for this answer but then I realised that it was wrong; each time "fooFactory" was called, all the prototype's methods were recreated from scratch. So, it doesn't give any advantage over adding methods directly to the object, this only leads to confusion. – Serg Oct 28 '18 at 11:46
4

If you are willing to use ES2015 classes,

with ESNext, you can use Javascript private variables like this:

class Foo {
  #bar = '';
  constructor(val){
      this.#bar = val;
  }
  otherFn(){
      console.log(this.#bar);
  }
}

Private field #bar is not accessible outside Foo class.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Nitin Jadhav
  • 6,495
  • 1
  • 46
  • 48
4
function Foo(b)
{
   var bar = b;

   this.setBar = function(x){
        bar = x;
   }

   this.alertBar = function(){
        alert(bar);
   }
}

var test = new Foo(10);
alert(test.bar); // Alerts undefined
test.alertBar(); // Alerts 10
Paul
  • 139,544
  • 27
  • 275
  • 264
  • +1 Great example. Still confused on one thing. What makes `bar` in `var bar = b;` persistent? – Gary Jun 14 '13 at 12:17
  • 2
    Hi @Gary, hopefully I can explain that: Every time a `new Foo` is constructed a new `bar` is created within the function scope of that `Foo`'s constructor. That `bar` will exist in memory for as long as the `Foo` exists in memory (or a reference to any of the function in that `Foo` which all also have access to `bar`. – Paul Jun 14 '13 at 15:35
  • 1
    Just as every instance of `Foo` has its own `setBar` and `alertBar`, it has it's own `bar`, and in fact it's own `b` as well. See this JSFiddle for a slightly more concise version: http://jsfiddle.net/fzXZ6/4/ – Paul Jun 14 '13 at 15:36
0

In ES6+ terms the proper way to do this is as shown in the @Nitin Jadhav's answer. However if for some reason you would like to stick with the good old constructor functions it could be achieved like;

function Foo(val){
  function Construct(){};
  Construct.prototype = { set bar(_){} // you may throw an error here
                        , get bar(){return val;}
                        };
  return new Construct();
};

So two things happen here.

  1. You don't populate the instances of Foo with properties like in the accepted answer.
  2. Unlike the Private Class Fields abstraction you are free to throw an error or not when someone tries to access the private variable through the setter.

Perhaps you would like a private field of the instance itself instead of accessing it through the prototype. Then you may do like;

function Foo(val){
  Object.defineProperty(this,"bar",{ set: function(){}
                                   , get: function(){return val;}
                                   });
};
Redu
  • 25,060
  • 6
  • 56
  • 76
-2

I recently had a similar issue but wanted to use accessor properties also. Below is a Foo(Bar) example based on what I came up with for a solution. This example is trivial but can easily be expanded upon using more complex get/set functions.

function Foo(Bar){
    Object.defineProperty(this,"bar",{get:function(){return Bar},set:function(val){Bar=val}});

}
x=new Foo(3);
y=x.bar; //3
x.bar++; //x.bar==4
  • @Gary Javascript is case sensitive. Bar and bar are two separate properties. Bar is private to Foo whereas bar is a public property that provides access to Bar. This accomplishes the same thing as the accepted answer. – Peter Schweitzer Jr Nov 18 '16 at 19:12