Making JavaScript private methods accessible to its public methods - javascript

I understand there are couple of patterns to make JavaScript 'class-like'.
I would like to take the 'extending by prototype' way... simply because it looks more neat. I am not worried about performance much here...
In the below example I have a class (basically function) MetricsChart. I have couple of public methods and one private method (basically a reusable method).
Here from the public method (drawOrAdd) I can't access the private method (_convertArrayToTable), how can I do that?
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
}
MetricsChart.prototype.drawOrAdd = function(data)
{
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
MetricsChart.prototype.isChartDrawn = function () {
return this._isChartDrawn;
}
MetricsChart.prototype.getChartData = function () {
}
One way I accidentally found was to enclose the public methods inside the MetricsChart class itself...
It works for me :): I can access the public methods outside and the public method can access the private method (serves the purpose).
Below code... Is this right? Am I doing anything wrong?
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
MetricsChart.prototype.drawOrAdd = function (data) {
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
MetricsChart.prototype.isChartDrawn = function () {
return this._isChartDrawn;
}
MetricsChart.prototype.getChartData = function () {
}
}

So, here a couple of things, in order to understand what you have done precisely.
First of all:
function foo() {
var i = 0;
function bar() {
return true;
}
}
What's happening here: every time the function foo is called, it creates in its scope a new variable i, and a new function bar. The function bar and the variable i are in its scope, it means they're local: there is no way, with this code, to access to either i or bar outside the function foo. Also because, once the function foo is terminated, both i and bar are disposed.
So, this is why you cannot access from your "public" method to the "private" one, and I hope it's more clear now. The only way for a function to access to a function or variable is that there is a reference shared in the same scope. So, this is what you have done in your last example: you define your "public" methods in the same scope where you define your "private" method. In this way they can access each other. However, the way you have done, has a big downside. As I said previously, the function bar is created every time the function foo is called. In a "class" example, it means:
function MyClass() {
function myprivate() {}
MyClass.prototype.mypublic = function () { return myprivate() }
}
It means that every time you're creating an instance of MyClass, you're creating two new functions, and you're rewrite all the time the prototype of your "class". This is far from be a good approach. In fact, if you have something like:
var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // false
console.log(_mypublic === a.mypublic) // false too!
So, you guess right but you executed wrong. What you need here is a the "module pattern": nowadays you can use CommonJS module in nodejs or AMD in browser and so on, but the basic idea is defined a "scope" and exports from this scope only what you want. In your case, you could have:
// this is your "module"
;(function(exports) {
// your local (private) function
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
}
MetricsChart.prototype.drawOrAdd = function(data) {
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
// you decided to exports to the main scope what you want
exports.MetricsChart = MetricsChart;
}(this)); // here `this` is the global object
And that's it. You have created a closure, using the "module pattern", and from the "public" method you can access to the "private" function, because they're defined in the same scope. But because you do not do that in the "class" constructor, you don't redefine them every time you instantiate a new object. Therefore, the previous example written in this way, will give the right result:
var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // true
console.log(_mypublic === a.mypublic) // true

What you've done isn't necessarily "wrong"...it just looks weird. Also, you won't be able to access "MetricsChart.prototype.*" until after you've created an instance of "MetricsChart". Depending on how you are using this object, it may not matter.
That being said, another way is to keep your original structure, but move the following outside of the constructor:
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
It would still be private to your module which should be good enough (you are using modules right?).

What you have done works perfectly.
You can't inherit private methods in any OOP language in terms of overriding them or accessing them directly. They are private. So it makes no sense to have them prototyped for inheritance purposes. You have wrapped them in function scope so they are as "private" as they need to be.

To access the private methods use privilege methods. Check this document: http://javascript.crockford.com/private.html.
About your code check this answer:
Setting javascript prototype function within object class declaration
p.s.
function Test()
{
var p = function(pv)
{
//
}
this.e = function (ap) { p(ap) }
}
var n = new Test();
n.e("e"); // It is legal call
n.p(); // will throw
But if you declare a private function in c-tor it will be executed on first creation of object of this type. When declare a methods in prototype this methods are add before any code execution. In general the browser first check the js file to collect all methods for prototype and than execute any code. So when you declare a prototype methods into c-tor this methods will be available only after first creation of the object of those type. ( Sorry for my English ).
Check this situation:
function Test()
{
alert(this.ST_A);//alert undefined
alert(this.ST_B);//alert 2
Test.prototype.ST_A = 1;
alert( this.ST_A)//alert 1
}
Test.prototype.ST_B = 2;
In first pass the browser will populate Test with ST_B and ST_B will be available anywhere any time. After than in second pass the browser will start to execute the code in this time ST_A will not be visible until the browser execute the Test.prototype.ST_A = 1;

Related

Creating JS library with dynamic function calling

I'm creating a JS 'library' file, But I want to encapsulate it in it's entirety within an object, to avoid contaminating namespace of the pages that include the file
The twist to this is within a function inside the library I need to call others functions within library by name, eg using window[]
The code below is just a sample there would actually be several hundred functions that could be called by name. It's this that's caused by trouble as I can't get window[] to reference the function, what's the right way to go about this?
I have tried this, in host page:
<script src= "mylib.js"></script>
var oMyLib = new cMyLib(); //there will only ever be one 'instance' of this
In mylib.js everything is contained in one function:
function cMyLib() {
this.doStuff = function () {
someFunc(this); //call another function in the lib
}
// I tried it with prototypes also
// cMyLib.prototype.doStuff = function () {
// someFunc();
// }
function someFunc(that) {
var s='anotherFunc1'
var f = window[s]; //undefined!
f();
s='anotherFunc2'
f=window[s];
f();
}
function anotherFunc1() {}
function anotherFunc2() {}
}
The functions that you want to reference by name (or actually by number, according to your comments) should be part of that object, and not accessed via window, e.g.:
function cMyLib() {
// allow call without new
if (! (this instanceof cMyLib)) {
return new cMyLib();
}
// enforce singleton
if (this.constructor.singleton) {
return this.constructor.singleton;
} else {
Object.defineProperty(this.constructor, 'singleton', {
value: this
});
}
// instruction array (no need to expose via `this`)
var insn = [];
insn[0x4c] = function lda_immediate() { ... }
// instruction execution
this.step = function() {
var opcode = memory[pc++];
if (opcode in insn) {
// `.call` ensures `this` is set inside the instruction fn.
insn[opcode].call(this);
} else {
hcf();
}
}
}
Note the extra stuff at the top - convenience code to ensure that only one cMyLib can exist.
As long as a function is in parent scope you can just reference it directly, i.e.
function someFunc(that) {
anotherFunc1();
};
will simply work.
Another thing is that classical way to do this is to wrap everything in a self-calling anonymous function, i.e.
(function() {
function anotherFunc1() {};
function anotherFunc2() {};
this.cMyLib = function() { ... };
})();
But your approach is fine as well.
If you wish to call your functions by dynamic name, then you can store them in a top level object:
(function() {
var my_functions = {
anotherFunc1: function() {},
anotherFunc2: function() {}
};
this.cMyLib = function() {
var name = 'anotherFunc1';
my_functions[name]();
};
})();
It's like creating an isolated "global" scope.
Side note: Do not use eval. It is very unsafe and slow. It will backfire at you at some point.

Put function in another scope?

I have a class that (very) simplified looks like this:
var myClass = (function( window ) {
function myClass(configObject) {
var instanceVar = 'something that is different for every instance of myClass';
configObject.action && configObject.action();
}
return myClass;
})(window);
Now when I pass the option config object (which may contain a theoretically unlimited number of functions that in turn reference variables from the class that are different for every instance of that class) ...
var config = {
"action": function() {
concole.log(instanceVar); // is undefined when called from myClass, because it's in another scope
}
};
var instance = new myClass(config);
... of course those vars are not accessible in the function scope. How can I put the functions from the config into the class scope?
bindcame to my mind, but it only changes the thiscontext, not the scope. I could pass the variable as argument to the function, but then every function would have to check for that argument inside, and with regard to the number of functions coming from the external configuration object that may be out of my control, I would like to prevent making this a prequisite.
Is there a way to "clone" the functions into class scope or anything like that? Could closures be helpful in any way (and if so, please exemplify, as I haven't wrapped my mind around closures completely yet).
Thanks!
Here is my version
var myClass = (function( global ) {
function myClass(configObject) {
var instanceVar = 'something that is different for every instance of myClass';
configObject.action && configObject.action(instanceVar);
}
return myClass;
})(window);
var instance = myClass({
"action": function(instanceVar) {
console.log(instanceVar);
}
});
js fiddle: https://jsfiddle.net/e0pgs4vw/
instanceVar is defined inside a closure, so you can't access it from outside unless explicitly passed.
To prevent the need for passing it to each method, you can create a new configInstance from configObject, and add instanceVar as property to the new instance:
var myClass = (function(window) {
function myClass(configObject) {
var instanceVar = 'something that is different for every instance of myClass';
var configInstance = Object.create(configObject, { // create a new configInstance that inherits from the origin configObject, which means that the original won't be changed
instanceVar: {
writable: false,
configurable: false,
value: instanceVar
} // add instanceVar as a member to configInstance
});
configInstance.action && configInstance.action();
}
return myClass;
})(window);
var config = {
"action": function() {
console.log(this.instanceVar);
}
};
var instance = new myClass(config);

Writing JS Prototypes, should all functions use the Prototype object?

I'm beginning to learn more about writing JS using the Prototype object, but I want to make sure I don't pick up any bad habits from other developers. My understanding of using Prototype is to create public methods for your instance. For example:
var module = new Module();
module.method();
But I see a lot of developers creating all their code inside the Prototype object, things that I would consider "private". Is this bad practice or considered okay? It just means I can then do:
module.privateFn();
Do they know this? Is that okay? Any help appreciated. I've been looking through the source code on GitHub to try establish the best way forward, here's a script that uses Prototypes for everything (for instance attachEvent which they clearly want privately kept):
https://github.com/WickyNilliams/headroom.js/blob/master/dist/headroom.js
Much appreciated, I want to make sure I develop using the correct implementations.
First of all you don't need to write modules using prototype. Think like if you writing something like a class you should use prototypes. And also it's important to where define your methods. Defining methods on prototype object and defining them in constructor function is totally different things!
Let's see a sample class definition with using methods defined in constructor:
var Dog = (function () {
var Dog = function (age, name) {
var that = this;
this.age = age;
this.name = name;
this.sayHi = function () {
console.log('Warf! Im ' + that.name); // meaning of "this" changed!!!
};
this.anotherMethod = function () {};
};
return Dog;
}());
var puppy = new Dog(1, 'puppy'); // sayHi and anotherMethod created
var sirius = new Dog(1, 'sirius'); // sayHi and anotherMethod recreated
sirius.sayHi = function () { console.log('Yohalolop!'); };
puppy.sayHi(); // -> 'Warf! Im puppy'
sirius.sayHi(); // -> 'Yohalolop!'
So there is some problems with the above example, firstly methods are defined like any other instance variables. Actually yeah you define them as instance variable and this means this functions are recreated for every instance object you create. I guess you have mentioned you can't use this keyword in your method definitions. This is error prone and there is a chance to forget that and use this keyword by mistaken. There are some times you can use methods as instance variables of course like variable callbacks.
Let's see a sample class definition with prototype object:
var Dog = (function () {
var Dog = function (age, name) {
this.age = age;
this.name = name;
};
// sayHi method defined only once in prototype
Dog.prototype.sayHi = function () {
console.log('Warf! Im ' + this.name; // we can use this keyword
};
// anotherMethod defined only once in protoype
Dog.prototype.anotherMethod() {
};
return Dog;
}());
var puppy = new Dog(1, 'puppy');
var sirius = new Dog(1, 'sirius'); // sirius and puppy sharing same prototype object
puppy.sayHi(); // -> 'Warf! Im puppy'
sirius.sayHi(); // -> 'Warf! Im sirius'
// remember puppy and sirius sharing same prototype object
Dog.prototype.sayHi = function () {
console.log('Yohalolop');
};
puppy.sayHi(); // -> 'Yohalolop'
sirius.sayHi(); // -> 'Yohalolop'
As an answer to your question about private functions, it is more complicated. Yes you can use private functions even you define your methods on prototype, but there are some concerns about testing. Usage of them is up to you. I prefer to don't use. Let me show some examples.
var Calculator = (function () {
var Calculator = function () {
this.importantNumber = 2;
};
// There is unfortunately no native implementation
// for private methods but you can mimic them with
// unaccessible functions and binding.
var someExtremeComputations = function () {
return 40 + this.importantNumber; // this keyword points to instance because of binding
};
Calculator.prototype.getMeaningOfLife = function () {
var result = someExtremeComputations.call(this); // we bind function to instance
return result;
};
return Calculator;
}());
This is the one of the examples how you can define private methods in javascript. The problem with private functions, they can't be tested. There is no way to test someExtremeComputations method.
Some people (includes me) use prefixed underscore naming convention for private methods. So they are actually public methods but if someone calling them or overriding they were warned by prefixed underscore. After all we can test private methods since they are public in real.
var Calculator = (function () {
var Calculator = function () {
this.importantNumber = 2;
};
// private method's name prefixed by an underscore to warn
// other developers to be careful about that or not to use.
Calculator.prototype._someExtremeComputations = function () {
return 40 + this.importantNumber;
};
Calculator.prototype.getMeaningOfLife = function () {
var result = this.someExtremeComputations(); // no need to bind
return result;
};
return Calculator;
}());
Explaining this with a few words is impossible. A generally good pattern is to construct methods through prototypes when you want to optimize your code. A good guideline is to only put the most essential data in the memory, using prototypes is critical for this since the prototyped variables and methods isn't injected into the memory until you request them.
When it comes yo your example there are no prototypes.
Simple example
// new object
var Dog = function() {
var that = this;
// add a property
that.name = "Fido";
// add a method
that.getName = function() {
return that.name;
};
};
// ... all the above is stored in memory directly
// Requires to be constructed
var dogObj = new Dog();
console.log(dogObj.getName()); // Fido
delete Dog.name // false
typeof Dog.name // "string"
delete dogObj.name // true
typeof dogObj.name // "undefined"
typeof Dog.name // "string" (still there)
// Will be available in the dogObj (after you call it)
dog.prototype.first = "first";
// Will be available in the dogObj (after you call it)
dog.prototype.second = function() {
return "second";
}
// Will not be available in dogObj
dog.third = "third";

Better way to access private members in Javascript

After reading a bit on Javascript's prototypical inheritance model, I change my style of constructing a class from
var Some_Class = function() {
this.public_method = function() {
};
(function() {
// constructor
}).call(this)
}
to
var Some_Class = function() {
(function() {
// constructor
}).call(this)
}
Some_Class.prototype.public_method = function() {
};
Although I understand that this is a good practice, but I am not allowed to access private methods from the public method anymore
var Some_Class = function() {
var private_member = 'whatever';
(function() {
// constructor
}).call(this)
}
Some_Class.prototype.public_method = function() {
return private_member; // not possible
};
After reading through an article here (Closure-created constructor), then I came out with this
var Some_Class = function() {
var private_member = 'whatever',
private_method = function(_some_value) {
// private method implementation
};
if(!arguments.callee.prototype.public_method) {
arguments.callee.prototype.public_method = function() {
private_method.call(this, private_method);
};
}
(function() {
// constructor
}).call(this)
}
However, what are the drawbacks of doing this?! or is there a better way of doing this if I want to access private member in the public method?
My answer is a non-answer: there's no built-in private access in JavaScript but that's okay because YAGNI. Here's how I make private members in my code:
function Some_Class() {
this._private_member = 'whatever';
}
Some_Class.prototype._private_method = function() {
};
That's good enough. It's not really worth it to jump through hoops when the only real purpose of private is to protect yourself from... yourself.
(I say this having spent many hours myself playing around with every permutation of closures and prototyping, just as you are, and finally saying "screw it, it's not worth it".)
The use of function scope variables and closures to simulate private variables/functions is a well established idiom in the javascript community. If the variable is truly intended to be private, I see no drawback to this approach (although some claim that performant code on certain browsers/hosts has to pay attention to how many closures get created).
In your example, the private_method (and its environment) is shared across all objects - since your public_method closure is created only the first time the object is constructed (and bound to the constructor's prototype property that sets the created object's internal prototype chain) - so the private_method that is used is only the one that was created the first time.
Here is some sample code that will help illustrate what is going on:
var global = 1;
var Some_Class = function() {
var private_method = 'whatever';
var now = ++global;
print("outer now: " + now );
private_method = function(_some_value) {
// private method implementation
print("inner now: " + now);
};
if(!arguments.callee.prototype.public_method) {
arguments.callee.prototype.public_method = function() {
private_method.call(this, private_method);
};
}
(function() {
// constructor
}).call(this)
}
new Some_Class().public_method(); // outer now: 2, inner now: 2
new Some_Class().public_method(); // outer now: 3, inner now: 2
new Some_Class().public_method(); // outer now: 4, inner now: 2
Are you sure that is what you want?
If your private_method does not need to refer to the enclosing object's state, then I see little benefit in doing things the way you are doing.
What I usually do (if i have to use 'new' to create my object) is the following:
function MyClass() {
var private_var = 1;
function private_func()
{
}
this.public_func = function()
{
// do something
private_func();
}
this.public_var = 10;
}
var myObj = new MyClass();
The downside to this approach is that each time you construct the object via 'new' you re-create all the closures. But unless my profiler tells me that this design choice needs to be optimized, i prefer its simplicity and clarity.
Also I don't see the benefit in your code of doing the following either:
(function() { }).call(this); // call the constructor
Why are you creating a separate scope in your constructor?
If you have not done so already have a look at this JavaScript Module Pattern, which allows you to access private methods and variables from within the public functions, etc.
Echoing John Kugelman: It's impossible to create private members in javascript. Live with it. Even if you create a enclosure like this:
function SomeClass() {
var _private = 0;
this.public_acessor = function() {return _private};
}
Any user can still write:
SomeClass._not_private_anymore = 1;
SomeClass.public_acessor = function () {return this._not_private_anymore};
In the end, you can't trust any public member to be the same you declared. If someone is up to break your code, he will! Another user won't break your code only because it's useful.
Works with prototype, not just singleton. Problem is, when it's time to subclass, my subclass has no access to the privates

Encapsulation in javascript

I need to create simple reusable javascript object publishing several methods and parameterized constructor. After reading through several "OOP in JavaScript" guides I'm sitting here with an empty head. How on the Earth can I do this?
Here my last non-working code:
SomeClass = function(id) {
this._id = id;
}
(function() {
function intFun() {
return this._id;
}
SomeClass.prototype.extFun = function() {
return incFun();
}
})();
This is my usual approach:
MyClass = function(x, y, z) {
// This is the constructor. When you use it with "new MyClass(),"
// then "this" refers to the new object being constructed. So you can
// assign member variables to it.
this.x = x;
...
};
MyClass.prototype = {
doSomething: function() {
// Here we can use the member variable that
// we created in the constructor.
return this.x;
},
somethingElse: function(a) {
}
};
var myObj = new MyClass(1,2,3);
alert(myObj.doSomething()); // this will return the object's "x" member
alert(myObj.x); // this will do the same, by accessing the member directly
Normally the "this" keyword, when used in one of the object's methods, will refer to the object itself. When you use it in the constructor, it will refer to the new object that's being created. So in the above example, both alert statements will display "1".
An exception to this rule is when you pass one of your member functions somewhere else, and then call it. For example,
myDiv.onclick = myObj.doSomething;
In this case, JavaScript ignores the fact that "doSomething" belongs to "myObj". As a result, the "this" inside doSomething will point to another object, so the method won't work as expected. To get around this, you need to specify the object to which "this" should refer. You can do so with JavaScript's "call" function:
myDiv.onclick = function() {
myObj.doSomething.call(myObj);
}
It's weird, but you'll get used to it eventually. The bottom line is that, when passing around methods, you also need to pass around the object that they should be called on.
I usually don't worry too much about hiding the internals, although I do prefix them with underscores to mark them as not intended to be used outside the "class". Normally what I will do is:
var MyClass = function() {};
MyClass.prototype = {
_someVar : null,
_otherVar : null,
initialize: function( optionHash ) {
_someVar = optionsHash["varValue"];
_otherVar = optionsHash["otherValue"];
},
method: function( arg ) {
return _someVar + arg;
},
};
And use it as so...
var myClass = new MyClass( { varValue: -1, otherValue: 10 } );
var foo = myClass.method(6);
All vars are private:
SomeClass = function (id) {
var THIS = this; // unambiguous reference
THIS._id = id;
var intFun = function () { // private
return THIS._id;
}
this.extFun = function () { // public
return intFun();
}
}
Use THIS within private methods since this won't equal what you might expect.
From http://learn.jquery.com/code-organization/concepts/#the-module-pattern:
// The module pattern
var feature = (function() {
// private variables and functions
var privateThing = "secret";
var publicThing = "not secret";
var changePrivateThing = function() {
privateThing = "super secret";
};
var sayPrivateThing = function() {
console.log( privateThing );
changePrivateThing();
};
// public API
return {
publicThing: publicThing,
sayPrivateThing: sayPrivateThing
};
})();
feature.publicThing; // "not secret"
// logs "secret" and changes the value of privateThing
feature.sayPrivateThing();
So using returning an object that aliases its "methods" could be another way to do it.
I've read from http://www.amazon.com/Programming-Oracle-Press-Poornachandra-Sarang-ebook/dp/B0079GI6CW that it is always good practice to use getters and setters rather that accessing the variable directly from outside the object, so that would eliminate the need of returning variables by reference.
BTW you could just use this.variable to reference/declare a public variable and var variable to declare a private variable.
I know this is a late answer, but I hope it helps anyone who reads it in the future.

Categories

Resources