Accessing private member variables from prototype-defined functions

Accessing private member variables from prototype-defined functions

Is there any way to make “private” variables (those defined in the constructor), available to prototype-defined methods?
TestClass = function(){
var privateField = “hello”;
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};

This works:
t.nonProtoHello()

But this doesn’t:
t.prototypeHello()

I’m used to defining my methods inside the constructor, but am moving away from that for a couple reasons.

Solutions/Answers:

Solution 1:

No, there’s no way to do it. That would essentially be scoping in reverse.

Methods defined inside the constructor have access to private variables because all functions have access to the scope in which they were defined.

Methods defined on a prototype are not defined within the scope of the constructor, and will not have access to the constructor’s local variables.

You can still have private variables, but if you want methods defined on the prototype to have access to them, you should define getters and setters on the this object, which the prototype methods (along with everything else) will have access to. For example:

function Person(name, secret) {
    // public
    this.name = name;

    // private
    var secret = secret;

    // public methods have access to private members
    this.setSecret = function(s) {
        secret = s;
    }

    this.getSecret = function() {
        return secret;
    }
}

// Must use getters/setters 
Person.prototype.spillSecret = function() { alert(this.getSecret()); };

Solution 2:

Update: With ES6, there is a better way:

Long story short, you can use the new Symbol to create private fields.
Here’s a great description: https://curiosity-driven.org/private-properties-in-javascript

Example:

var Person = (function() {
    // Only Person can access nameSymbol
    var nameSymbol = Symbol('name');

    function Person(name) {
        this[nameSymbol] = name;
    }

    Person.prototype.getName = function() {
        return this[nameSymbol];
    };

    return Person;
}());

For all modern browsers with ES5:

You can use just Closures

The simplest way to construct objects is to avoid prototypal inheritance altogether.
Just define the private variables and public functions within the closure, and all public methods will have private access to the variables.

Or you can use just Prototypes

In JavaScript, prototypal inheritance is primarily an optimization. It allows multiple instances to share prototype methods, rather than each instance having its own methods.
The drawback is that this is the only thing that’s different each time a prototypal function is called.
Therefore, any private fields must be accessible through this, which means they’re going to be public. So we just stick to naming conventions for _private fields.

Related:  jQuery document.createElement equivalent?

Don’t bother mixing Closures with Prototypes

I think you shouldn’t mix closure variables with prototype methods. You should use one or the other.

When you use a closure to access a private variable, prototype methods cannot access the variable. So, you have to expose the closure onto this, which means that you’re exposing it publicly one way or another. There’s very little to gain with this approach.

Which do I choose?

For really simple objects, just use a plain object with closures.

If you need prototypal inheritance — for inheritance, performance, etc. — then stick with the “_private” naming convention, and don’t bother with closures.

I don’t understand why JS developers try SO hard to make fields truly private.

Solution 3:

When I read this, it sounded like a tough challenge so I decided to figure out a way. What I came up with was CRAAAAZY but it totally works.

First, I tried defining the class in an immediate function so you’d have access to some of the private properties of that function. This works and allows you to get some private data, however, if you try to set the private data you’ll soon find that all the objects will share the same value.

var SharedPrivateClass = (function() { // use immediate function
    // our private data
    var private = "Default";

    // create the constructor
    function SharedPrivateClass() {}

    // add to the prototype
    SharedPrivateClass.prototype.getPrivate = function() {
        // It has access to private vars from the immediate function!
        return private;
    };

    SharedPrivateClass.prototype.setPrivate = function(value) {
        private = value;
    };

    return SharedPrivateClass;
})();

var a = new SharedPrivateClass();
console.log("a:", a.getPrivate()); // "a: Default"

var b = new SharedPrivateClass();
console.log("b:", b.getPrivate()); // "b: Default"

a.setPrivate("foo"); // a Sets private to "foo"
console.log("a:", a.getPrivate()); // "a: foo"
console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is "foo"!

console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype
console.log(a.private); // undefined

// getPrivate() is only created once and instanceof still works
console.log(a.getPrivate === b.getPrivate);
console.log(a instanceof SharedPrivateClass);
console.log(b instanceof SharedPrivateClass);

There are plenty of cases where this would be adequate like if you wanted to have constant values like event names that get shared between instances. But essentially, they act like private static variables.

Related:  How to get the 'height' of the screen using jquery [duplicate]

If you absolutely need access to variables in a private namespace from within your methods defined on the prototype, you can try this pattern.

var PrivateNamespaceClass = (function() { // immediate function
    var instance = 0, // counts the number of instances
        defaultName = "Default Name",  
        p = []; // an array of private objects

    // create the constructor
    function PrivateNamespaceClass() {
        // Increment the instance count and save it to the instance. 
        // This will become your key to your private space.
        this.i = instance++; 
        
        // Create a new object in the private space.
        p[this.i] = {};
        // Define properties or methods in the private space.
        p[this.i].name = defaultName;
        
        console.log("New instance " + this.i);        
    }

    PrivateNamespaceClass.prototype.getPrivateName = function() {
        // It has access to the private space and it's children!
        return p[this.i].name;
    };
    PrivateNamespaceClass.prototype.setPrivateName = function(value) {
        // Because you use the instance number assigned to the object (this.i)
        // as a key, the values set will not change in other instances.
        p[this.i].name = value;
        return "Set " + p[this.i].name;
    };

    return PrivateNamespaceClass;
})();

var a = new PrivateNamespaceClass();
console.log(a.getPrivateName()); // Default Name

var b = new PrivateNamespaceClass();
console.log(b.getPrivateName()); // Default Name

console.log(a.setPrivateName("A"));
console.log(b.setPrivateName("B"));
console.log(a.getPrivateName()); // A
console.log(b.getPrivateName()); // B

// private objects are not accessible outside the PrivateNamespaceClass function
console.log(a.p);

// the prototype functions are not re-created for each instance
// and instanceof still works
console.log(a.getPrivateName === b.getPrivateName);
console.log(a instanceof PrivateNamespaceClass);
console.log(b instanceof PrivateNamespaceClass);

I’d love some feedback from anyone who sees an error with this way of doing it.

Related:  How to use setInterval function within for loop

Solution 4:

see Doug Crockford’s page on this. You have to do it indirectly with something that can access the scope of the private variable.

another example:

Incrementer = function(init) {
  var counter = init || 0;  // "counter" is a private variable
  this._increment = function() { return counter++; }
  this._set = function(x) { counter = x; }
}
Incrementer.prototype.increment = function() { return this._increment(); }
Incrementer.prototype.set = function(x) { return this._set(x); }

use case:

js>i = new Incrementer(100);
[object Object]
js>i.increment()
100
js>i.increment()
101
js>i.increment()
102
js>i.increment()
103
js>i.set(-44)
js>i.increment()
-44
js>i.increment()
-43
js>i.increment()
-42

Solution 5:

I suggest it would probably be a good idea to describe “having a prototype assignment in a constructor” as a Javascript anti-pattern. Think about it. It is way too risky.

What you’re actually doing there on creation of the second object (i.e. b) is redefining that prototype function for all objects that use that prototype. This will effectively reset the value for object a in your example. It will work if you want a shared variable and if you happen to create all of the object instances up front, but it feels way too risky.

I found a bug in some Javascript I was working on recently that was due to this exact anti-pattern. It was trying to set a drag and drop handler on the particular object being created but was instead doing it for all instances. Not good.

Doug Crockford’s solution is the best.

Solution 6:

@Kai

That won’t work. If you do

var t2 = new TestClass();

then t2.prototypeHello will be accessing t’s private section.

@AnglesCrimes

The sample code works fine, but it actually creates a “static” private member shared by all instances. It may not be the solution morgancodes looked for.

So far I haven’t found an easy and clean way to do this without introducing a private hash and extra cleanup functions. A private member function can be simulated to certain extent:

(function() {
    function Foo() { ... }
    Foo.prototype.bar = function() {
       privateFoo.call(this, blah);
    };
    function privateFoo(blah) { 
        // scoped to the instance by passing this to call 
    }

    window.Foo = Foo;
}());