I\'m new to JavaScript and I\'m trying to wrap my head around creating \"classes\" with private data and public functions. I\'ve been told Immediately Invoked Function Expre
But that way you define .privilegedMethod()
each time an object is created and each of them will hold diferent version of the ( same purpose ) method...
Solution I came up with is to use object to object ( private ) hashmap, and map the newly created object to it's corresponding data in ctor function, and use hasmap as 'manager' to figure out which data correspond to which object, in prototype methods, something like this:
var Car =
( function ( hashmap ) {
function PrivateClassCarData ( c, t ) {
this.color = c;
this.type = t;
}
function Car ( color, type ) {
hashmap.place( this, new PrivateClassCarData( color, type ) );
}
// read
Car.prototype.getColor =
function () {
return hashmap.read( this ).color;
};
// write
Car.prototype.setColor =
function (c) {
hashmap.read( this ).color = c;
return this;
};
// weak point, memory leak source
// dereference object from hash-map before updating variable that points to it
// another reference is kept in hashmap
// @TODO, automatic dereferencing execution, anybody?
Car.prototype.mfree =
function () {
hashmap.drop( this );
return this;
};
return Car;
} )(
// basic hash-map implementation
// maps objects to objects
( function ( hk, hv ) {
return {
place : function ( objKey, objVal ) {
hk.push( objKey );
hv.push( objVal );
return this;
},
read : function ( objKey ) {
return hv[ hk.indexOf( objKey ) ];
},
drop : function ( objKey ) {
var pos;
( ( pos = hk.indexOf( objKey ) ) != -1 )
&& (
hk.splice( pos, 1 ),
hv.splice( pos, 1 )
);
return this;
}
};
} )( [], [] )
);
var c1 = new Car("red","ferrary");
var c2 = new Car("white","porche");
c1.getColor();
// red
c2.setColor("silver");
c1.getColor();
// red
c2.getColor();
// silver
//
var Car =
( function ( cardb ) {
function Car ( color ) {
// facing the same problem here
// reinstaling .data() method for each created object
// but this way each has its own data store object
// and inner 1 to 1 circular reference js is able to deal with
cardb( this );
// set provided color parameter
this.data("color", color);
}
return Car;
} )(
// function to install .data() method to given object
// it gets attached to object directly, instead of
// attaching it to .prototype, in which case all
// object will access same data store
function ( obj ) {
var _data = {};
obj.data =
function ( name, value ) {
return arguments.length
? (
( value == null )
? _data[name]
: (
_data[name] = value,
this
)
)
: _data;
};
return obj;
}
);
var c1 = new Car("red");
var c2 = new Car("blue");
c1.data("color");
// red
c2.data("color");
// blue
c1.data("color","white");
c2.data("color");
// blue
c1.data("color");
// white
//
The only way to simulate private instance variables it to declare them as var myprivate
in the constructor function.
Any privileged method (=method that can access the private member) has to be declared within the constructor function's body as well so can't be on the prototype (will cost you extra cpu and memory and maybe doesn't optimize as well in some JS engines).
I never had a situation where it was needed to do this since in my opinion the cost is not worth the gain. Usually indicate to my future self and other programmers that a member is private by a widely used naming convention (name starts with underscore) _myPrivate
"Public override"'s answer inspired me to create the following code. Private instance members can be accessed publicly by ben._data.set
or you could re implement rules and or getters/setters so someone could still abuse it. It can still clean up you're object's publicly accessible members and making it easier to use the getters and setters.
//Namespacing DataStore to limit scope of the closures
var tools = {
DataStore : function(){
var store = [];
this.get = function(key){
return store[key];
};
this.set = function(key,value){
store[key] = value;
return value;
};
}
};
//Person constructor
var Person = function(name){
//you can access this member directly
// bob.name = "Lucy";
this.name=name;
//if having _data as not accesable by defining
// with var _data we whould have to define
// get and set here as this.get and this.set
this._data=new tools.DataStore();
};
//constant value used to get or set, for example:
//ben.get(ben.AGE);
//Could add this and rules to Person instead of Person.prototype
//then you'll need a helper function to set up inheritance
//to make sure the static's on Person are copied to it's children
Person.prototype.AGE=0;
//rules for getters and setters
//Will be a problem with inheritance if on prototype
//function Employee(name){Person.call(this,name);};
//Employee.prototype=Object.create(Person.prototype);
//Employee.prototype.rules["0set"]=..overwrites Person.prototype.rules["0set"]
//When inheriting you need to have a helper function set the rules for a child
//object
Person.rules = {}
//rule for AGE set
Person.rules[Person.prototype.AGE+"set"] = function(val){
var tmp;
tmp = parseInt(val);
if(isNaN(tmp)){
throw new Error("Cannot set the age of the person "+
"to non number value, value of age:"+val);
}
if(tmp>150){
throw new Error("Are you sure this is a person and "+
"not a turtule? Trying to set age to:"+val);
}
return this._data.set(this.AGE,tmp);
};
//rule for age get
Person.rules[Person.prototype.AGE+"get"] = function(){
return this._data.get(this.AGE);
};
Person.prototype.get = function(key){
return Person.rules[key+"get"].call(this);
};
Person.prototype.set = function(key,value){
return Person.rules[key+"set"].call(this,value);
};
var ben = new Person("Ben");
ben.set(ben.AGE,22);
console.log(ben.get(ben.AGE));
try{
ben.set(ben.AGE,151);
}catch(e){
console.log("error",e);
}
try{
ben.set(ben.AGE,"HELLO WORLD!");
}catch(e){
console.log("error",e);
}
Note of caution: Person.rules
needs to be copied to Child instances when you want to inherit from Person.
More about prototype, inheritance, overriding, calling super, multiple inheritance(mix in) and the value of this
here: https://stackoverflow.com/a/16063711/1641941