Should I create private variables/methods in this manner? - javascript

I am trying to learn more about how to create private variables and methods in JavaScript. The code below seems to work but I feel as though there may be a more efficient way to do this. Any suggestions?
var CountObject = (function () {
function countObject() {
// private variables
var _a = 1;
var _b = 2;
var _c = _a + _b;
// private method
addTo = function (num) {
_c = _c + _a + _b + num;
return _c;
}
}
// public method
countObject.prototype.add = function (num) {
return addTo(num);
};
return countObject;
}());
var testObject1 = new CountObject();
console.log(testObject1.add(1));
//output 7
console.log(testObject1.add(1));
//output 11
console.log(testObject1.add(1));
//output 15
var testObject2 = new CountObject();
console.log("testobject2:" + testObject2.add(100));
//output testobject2:106

addTo is not a private (local) method, but a global (not even instance-specific) function! Try to do testObject1.add(0) again after having created the testObject2 and you will see it fail.
You cannot call local (constructor-scoped) functions from the prototype, so this seems pointless. Fix it by using an instance-specific, privileged (with access to the local variables) method:
function CountObject() {
// private variables
var a = 1;
var b = 2;
var c = a + b;
// public method
this.add = function (num) {
c = c + a + b + num;
return c;
};
}

Related

In Javascript, is there a way to count how many created objects I've made?

For instance, let's say I'm really hungry so I just keep making pancakes!
var Buttermilk = new Pancake("Buttermilk", "Delicious");
var ChocolateChip = new Pancake("Chocolate Chip", "Amazing");
var BlueBerry = new Pancake("Blue Berry", "The Best");
var SnozBerry = new Pancake("Snoz Berry", "What's a Snoz Berry?");
How would I count how many pancakes I just made without manually doing it? Is there a code that says "There are this many variables that are of the Pancake variety"?
EDIT:
Thank you for the answers! I was specifically looking for a simple way to quickly count the amount of times I created an object with a small amount of code. And that is what I got, thank you!
You can have static properties in javascript classes. You can either hide them in closures that way:
var Pancake = (function() {
var instances = 0;
return function(a, b) {
this.a = a;
this.b = b;
instances++;
Pancake.prototype.instances = function() { // equivalent of a static method
return instances;
}
};
}());
or put them in the object prototype:
var pancake = function(a, b) {
this.a = a;
this.b = b;
pancake.prototype.count = pancake.prototype.count ? pancake.prototype.count + 1 : 1; // equivalent of a static property
}
You can also "override" the constructor, by implementing some kind of "inheritance", such as in this fiddle:
var Pancake = function(a, b) {
this.a = a;
this.b = b;
};
var before = Pancake.prototype;
var Pancake = function() {
console.log("overriden");
Pancake.prototype.instances = Pancake.prototype.instances ? Pancake.prototype.instances + 1 : 1; // here you should restore the whole prototype
return before.constructor.apply(this, arguments);
};
var a = new Pancake("a", "b");
document.write(Pancake.prototype.instances + "<br />");
var b = new Pancake("c", "d");
document.write(Pancake.prototype.instances + "<br />");
document.write(JSON.stringify(a) + "<br />");
document.write(JSON.stringify(b) + "<br />");
You can keep a counter which will get increment in constructor, here is a good solution
How can I count the instances of an object?
Use a counter variable inside the Pancake function.. :)
var count = 0;
function Pancake(){
// Cook pancakes
count += 1;
}
console.log('Total pancakes' + count);
I realise you've already accepted an answer but this took me a while! From your question I was thinking you may not want to change the Pancake class. So here's an attempt to avoid that.
This function will search all the objects in the object you specify and count all the instances of your type.
// Usage:
searchObjectForType(window, Pancake); // returns a number.
function searchObjectForType(obj,type){
// Keep track of objects in stack so we don't overflow by searching into the same objects;
var stackObjs = [];
function recursiveProbe(obj){
var foundCount = 0;
var objType;
// Some types will throw (iframes/nulls/..)
try{
objType = obj.toString();
}
catch(err){
return 0;
}
// Skip document/previous objects as we know they won't have any.
if(typeof obj === "string" || stackObjs.indexOf(objType)>=0 || obj===document){
return 0;
}
else{
stackObjs.push(objType);
}
for (var i in obj){
var prop = obj[i];
if(prop instanceof type){
foundCount++;
}
else{
foundCount += recursiveProbe(prop);
}
}
// Remove this type from stackObjs so we can search future types.
stackObjs.splice(stackObjs.indexOf(obj.toString()),1);
return foundCount;
}
return recursiveProbe(obj);
}
I'm sure there are cases where this fails, so feedback appreciated!
If you want to count the number of instances created from a prototype you need a property like:
Asset.prototype.index = 0;
Now, in the constructor itself use:
function Asset () {
this.index = Asset.prototype.index++;
}
a more object oriented approach would be to use a static method an a static property.
although JS doesn't have static property we can set it on constructor itself
e.g.
class Pancake {
static count() {
Pancake.counter = (Pancake.counter || 0) + 1;
return;
}
constructor(objName) {
Pancake.count();
this.name = objName;
}
}
new Pancake("A");
console.log(Pancake.counter); //1
new Pancake("B");
console.log(Pancake.counter); //2
new Pancake("C");
console.log(Pancake.counter); //3
new Pancake("D");
console.log(Pancake.counter); //4
new Pancake("E");
console.log(Pancake.counter); //5
demo : https://jsfiddle.net/mux2qnsc/

Get all instances of class in Javascript

I thought there would already be an answer for this but I can't seem to find one..
How can I run a particular class method on all instances of this class in Javascript?
This has to be done in a situation where I do not know the names of the instances.
I think I could use some sort of static variable inside my class to store all instances, but this doesn't seem to exist in JS
So how to call my method on all existing instances of my class?
Note : just for clarification : I'm not speaking about CSS classes, I'm speaking about objects.
Edit : By Class in Javascript, I mean the creation of a new object on a function:
function something()
{
}
var instance = new something();
You can create a static array and store it on your constructor function:
MyClass.allInstances = [];
MyClass.allInstances.push(this);
However, you need some way to figure out when to remove instances from this array, or you'll leak memory.
In Chrome 62+ you can use queryObjects from the console API - which will not work in native JavaScript code but in the console so it's great for debugging.
class TestClass {};
const x = new TestClass();
const y = new TestClass();
const z = new TestClass();
queryObjects(TestClass)
You'll have to provide a custom implementation.
I would do something like this :
function Class() {
Class.instances.push(this);
};
Class.prototype.destroy = function () {
var i = 0;
while (Class.instances[i] !== this) { i++; }
Class.instances.splice(i, 1);
};
Class.instances = [];
var c = new Class();
Class.instances.length; // 1
c.destroy();
Class.instances.length; // 0
Or like this :
function Class() {};
Class.instances = [];
Class.create = function () {
var inst = new this();
this.instances.push(inst);
return inst;
};
Class.destroy = function (inst) {
var i = 0;
while (Class.instances[i] !== inst) { i++; }
Class.instances.splice(i, 1);
};
var c = Class.create();
Class.instances.length; // 1
Class.destroy(c);
Class.instances.length; // 0
Then you could loop through all instances like so :
Class.each = function (fn) {
var i = 0,
l = this.instances.length;
for (; i < l; i++) {
if (fn(this.instances[i], i) === false) { break; }
}
};
Class.each(function (instance, i) {
// do something with this instance
// return false to break the loop
});
Sorry for such a late reply, but I found myself trying to achieve this and I think this may be a simpler answer.
Say you want all instances of class MyClass, only get instances created at top window level (not including instances created inside a closure):
for (var member in window)
{
if (window[member] instanceof MyClass)
console.info(member + " is instance of MyClass");
}
Keyword 'static' could be used in classes now (but check support), ...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static
class Point{
constructor(x, y){
this.x = x;
this.y = y;
Point.all.push(this);
}
destroy(){
let i = Point.all.indexOf(this);
Point.all.splice(i, 1);
}
static all = [];
}
var p1 = new Point(1, 2);
var p2 = new Point(54, 33);
var p3 = new Point(297, 994);
console.log(JSON.stringify(Point.all)); //[{"x":1,"y":2},{"x":54,"y":33},{"x":297,"y":994}]
p2.destroy();
console.log(JSON.stringify(Point.all)); //[{"x":1,"y":2},{"x":297,"y":994}]
You'll need to store a list of instances yourself:
function someClass(param) {
// add to all
if (this.constructor.all === undefined) {
this.constructor.all = [this];
} else {
this.constructor.all.push(this);
}
// set param
this.logParam = function() { console.log(param); };
}
var instance1 = new someClass(1);
var instance2 = new someClass(2);
for (var i = 0; i < someClass.all.length; i++) {
someClass.all[i].logParam();
}
If memory leaks are a concern then you can create a method for deleting instances when you are done with them:
function someClass(param) {
...
this.destroy = function() {
var all = this.constructor.all;
if (all.indexOf(this) !== -1) {
all.splice(all.indexOf(this), 1);
}
delete this;
}
}

can a function reference itself in javascript?

Suppose we define a function that simply increments its input by some stored value dd:
var obj={}
obj.dd=1
obj.f=function(x){
return x+this.dd
}
Alternatively you could create a closure for dd as follows but this would create a static increment as opposed to one that could be altered later:
var dd=1
var f=function(x){
return x+dd
}
We could alternatively store dd in the function itself:
var obj={}
obj.f=function(x){
return x+this.f.dd
}
obj.f.dd=1
I am curious as to whether it is possible for a function to retrieve a variable attached to itself without going through a parent object, something like a self keyword that would refer to the function itself and would allow the following:
var f=function(x){
return x+self.dd
}
f.dd=1
I know it is unnecessary to do such a thing but I think it would be cool if you could.
You can give function literals a name:
var f = function me(x) {
return x + me.dd;
};
f.dd = 1;
This doesn’t work properly in older versions of IE/JScript, though, as me and f don’t reference the same object. The (deprecated and not usable in strict mode) alternative is arguments.callee:
var f = function(x) {
return x + arguments.callee.dd;
};
f.dd = 1;
Also, your note about the closure isn’t quite right; it can be altered later, even through another function:
var dd = 1;
var f = function(x) {
return x + dd;
};
var setdd = function(_dd) {
dd = _dd;
};
A function is an object. If you reference the var holding the function:
var f = function (x) {
return x + f.dd
};
f.dd = 1;
alert(f(1));
result: 2
If the function is named, you can do the same:
function foo(x) {
return x + foo.dd;
}
foo.dd = 1;
alert(foo(1));
result: 2

Is it possible to make a JavasScript function act as if it was a string, no ()

Is this possible or am I barking up the wrong tree here?
var data = 'one';
function fnc(){
this.out = function(){
return data;
}
}
var instance = new fnc();
alert(instance.out);
data = 'two';
alert(instance.out);
// I know that this would achieve that, but that's not what I would like to know.
alert(instance.out());
data = 'two';
alert(instance.out());
Update:
The object which fnc is supposed to represent is actually a Sarissa dom document. Here is a more elaborate version of fnc(), dom_doc(). The accepted answer below has been integrated into the function below.
function get_doc(dom_node) {
var doc;
if (navigator.userAgent.indexOf("MSIE") >= 0) {
doc = new ActiveXObject("Msxml2.DOMDocument.3.0");
doc.loadXML(document.getElementById(dom_node).text);
}
else {
doc = Sarissa.getDomDocument();
doc = (new DOMParser()).parseFromString(document.getElementById(dom_node).textContent, "text/xml");
// runs XSLTProcessor in modern browsers as if it was trasformNode
doc.transformNode = function (stylesheet) {
var processor = new XSLTProcessor();
processor.importStylesheet(stylesheet);
return new XMLSerializer().serializeToString(processor.transformToDocument(this));
}
// allows modern browsers to extract xml the way the legacy IEs did
var getXML = {};
getXML.toString = function(){
return new XMLSerializer().serializeToString(doc);
};
doc.xml = getXML;
}
return doc;
}
Demo: JSFIDDLE
You seem to be talking about a getter method. If that's what you mean, you can use this:
var data = 'one';
function fnc(){
Object.defineProperty(this, 'out', {
get : function(){ return data; }
});
}
var instance = new fnc();
alert(instance.out);
http://jsfiddle.net/zztYd/1
This is not compatible with older browsers (see the compatibility table at the bottom of this page).
Also, it's a little weird to use a getter to fetch a global variable. Usually you use that to get the value of a private variable on your instance object, and in that case you could only modify it with a corresponding setter.
The alternative to bfavaretto's solution is to use the literal syntax for an object. Has almost the same level of support as Object.defineProperty().
Also, you should call Object.defineProperty only once, not with every instantiation.
I'm also going to provide a more realistic example, that is, the property gets calculated, instead of just referencing a global.
http://jsfiddle.net/mendesjuan/zztYd/3/
function Pair(a,b){
this.a = a;
this.b = b;
}
Pair.prototype = {
get sum(){
return this.a + this.b;
}
};
var pair = new Pair(1,2);
alert(pair.sum);
pair.a = 5;
alert(pair.sum);
The benefit of this is that you can change the implementation to be storage instead of calculation and you won't have to change how it's called.
function Pair(a,b){
this.a = a;
this.b = b;
this.sum = a + b;
}
Pair.prototype = {
setA: function(a){
this.a = a;
this.sum = this.a + this.b;
}
};
var pair = new Pair(1,2);
alert(pair.sum);
pair.setA(5);
alert(pair.sum);
Notice that you do have to now call setA so that sum can be recalculated. Or you could use a setter.
function Pair(a,b){
this.a = a;
this.b = b;
this.sum = a + b;
}
Pair.prototype = {
set a(a) {
this.a = a;
this.sum = this.a + this.b;
}
};
var pair = new Pair(1,2);
alert(pair.sum);
pair.a = 5;
alert(pair.sum);
jsFiddle Demo
These types of workarounds really bypass convention though. It should not be that much of a hamper to simply use () on your functions. Using () is expected, readable, best practice, and the industry standard.
Not sure why smartcaveman decided to remove his answer of using toString but that is a viable approach although somewhat hackish.
var data = 'one';
function fnc(){
var getData = {};
getData.toString = function(){
return data;
};
this.out = getData;
}
var instance = new fnc();
alert(instance.out);//one
data = 'two';
alert(instance.out);//two
var s = instance.out;
alert(s);//two

How to have function recall private variable between invocations

Here's an easy one straight from the text book I can't seem to find.
I have a javascript function. I want it to contain a private variable which remembers its value between invocations.
Can someone jog my memory please.
Create it using a closure:
function f() {
var x = 0;
return function() {return x++;};
}
Then use it as follows:
> g = f()
function () {return x++}
> g()
0
> g()
1
> g()
2
var accumulator = (function() {
var accum = 0;
return function(increment) {
return accum += increment;
}
})();
alert(accumulator(10));
alert(accumulatot(15));
Displays 10 then 25.
I am not sure if I understood correctly but maybe something like this would do the trick :
function Foo() {
var x = "some private data";
return {
getPrivateData : function(){
return x;
}
};
};
var xx = new Foo();
xx.getPrivateData();
Here is a truly private implementation
(function() {
var privateVar = 0;
window.getPreviousValue = function(arg) {
var previousVal = privateVar;
privateVar = arg;
return previousVal;
}
})()
alert(getPreviousValue(1));
alert(getPreviousValue(2));
Cheers

Categories