Is there any way to make “private” variables (those defined in the constructor), available to prototype-defined methods?
TestClass = function(){
var priv
In current JavaScript, I'm fairly certain that there is one and only one way to have private state, accessible from prototype functions, without adding anything public to this
. The answer is to use the "weak map" pattern.
To sum it up: The Person
class has a single weak map, where the keys are the instances of Person, and the values are plain objects that are used for private storage.
Here is a fully functional example: (play at http://jsfiddle.net/ScottRippey/BLNVr/)
var Person = (function() {
var _ = weakMap();
// Now, _(this) returns an object, used for private storage.
var Person = function(first, last) {
// Assign private storage:
_(this).firstName = first;
_(this).lastName = last;
}
Person.prototype = {
fullName: function() {
// Retrieve private storage:
return _(this).firstName + _(this).lastName;
},
firstName: function() {
return _(this).firstName;
},
destroy: function() {
// Free up the private storage:
_(this, true);
}
};
return Person;
})();
function weakMap() {
var instances=[], values=[];
return function(instance, destroy) {
var index = instances.indexOf(instance);
if (destroy) {
// Delete the private state:
instances.splice(index, 1);
return values.splice(index, 1)[0];
} else if (index === -1) {
// Create the private state:
instances.push(instance);
values.push({});
return values[values.length - 1];
} else {
// Return the private state:
return values[index];
}
};
}
Like I said, this is really the only way to achieve all 3 parts.
There are two caveats, however. First, this costs performance -- every time you access the private data, it's an O(n)
operation, where n
is the number of instances. So you won't want to do this if you have a large number of instances.
Second, when you're done with an instance, you must call destroy
; otherwise, the instance and the data will not be garbage collected, and you'll end up with a memory leak.
And that's why my original answer, "You shouldn't", is something I'd like to stick to.
By using a simple pattern based in ES6 WeakMaps is possible to obtain private member variables, reachable from the prototype functions.
Note : The usage of WeakMaps guarantees safety against memory leaks, by letting the Garbage Collector identify and discard unused instances.
// Create a private scope using an Immediately
// Invoked Function Expression...
let Person = (function() {
// Create the WeakMap that will hold each
// Instance collection's of private data
let privateData = new WeakMap();
// Declare the Constructor :
function Person(name) {
// Insert the private data in the WeakMap,
// using 'this' as a unique acces Key
privateData.set(this, { name: name });
}
// Declare a prototype method
Person.prototype.getName = function() {
// Because 'privateData' is in the same
// scope, it's contents can be retrieved...
// by using again 'this' , as the acces key
return privateData.get(this).name;
};
// return the Constructor
return Person;
}());
A more detailed explanation of this pattern can be found here
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()); };
Here's what I came up with.
(function () {
var staticVar = 0;
var yrObj = function () {
var private = {"a":1,"b":2};
var MyObj = function () {
private.a += staticVar;
staticVar++;
};
MyObj.prototype = {
"test" : function () {
console.log(private.a);
}
};
return new MyObj;
};
window.YrObj = yrObj;
}());
var obj1 = new YrObj;
var obj2 = new YrObj;
obj1.test(); // 1
obj2.test(); // 2
the main problem with this implementation is that it redefines the prototypes on every instanciation.
You need to change 3 things in your code:
var privateField = "hello"
with this.privateField = "hello"
.privateField
with this.privateField
.privateField
with this.privateField
.The final code would be the following:
TestClass = function(){
this.privateField = "hello";
this.nonProtoHello = function(){alert(this.privateField)};
}
TestClass.prototype.prototypeHello = function(){alert(this.privateField)};
var t = new TestClass();
t.prototypeHello()
@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;
}());