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;
}
}
Related
I'm building a small world builder in JavaScript (part of a larger simulation).
I'm trying to define an object's property in the constructor function by assigning a functions output to it.
In the code below, 'this.identifier' executes like a charm, but I want to assign more complex functions to for instance 'this.gender'.
In 'this.gender' I want to use math.random.math.floor to cycle through an array (that has two values, male and female).
When I write the actual function, 'this.gender' is dropped from the new Human object.
{
"identifier":42,
"lifestate":"Alive",
"health":"100",
"age":"10",
"metabolism":"12",
"econsumption":"11111",
"parent":"yes"
}
gender is dropped the moment I change it to a function.
I've tried using a return statement, but it makes no difference.
class Bluehuman {
constructor() {
this.identifier = Math.floor((Math.random() * 100));
this.lifestate = 'Alive';
this.health = '100';
this.age = '10';
this.metabolism = ['Low','Medium','High'];
this.econsumption = '11111';
this.parent = ['Yes','No'];
this.gender = ['Male','Female']; // Want to change this to a function without dropping from the new Bleuhuman object
}
}
var bluehuman = {};
var bluehumans = [];
for (var i = 0; i < 10; i++) {
bluehuman[i] = new Bluehuman();
bluehumans.push(bluehuman[i]);
}
var arrayPrint = JSON.stringify(bluehumans);
console.log(arrayPrint)
How can I assign the output of a function to 'this.gender' without having it dropped from the new bluehuman object?
You don't need a function, an expression is just fine to solve your problem
class Bluehuman {
constructor() {
this.identifier = Math.floor((Math.random() * 100));
this.lifestate = 'Alive';
this.health = '100';
this.age = '10';
this.metabolism = ['Low','Medium','High'];
this.econsumption = '11111';
this.parent = ['Yes','No'];
this.gender = ['Male','Female'][Math.round(Math.random())];
}
}
var bluehuman = {};
var bluehumans = [];
for (var i = 0; i < 10; i++) {
bluehuman[i] = new Bluehuman();
bluehumans.push(bluehuman[i]);
}
var arrayPrint = JSON.stringify(bluehumans);
console.log(arrayPrint)
You can just assign a function as any other value
this.test = function() { };
you will then be able to call it as:
new Bluehuman().test();
and if you log it to the console directly, you'll also see it:
console.log(new Bluehuman());
If you however call JSON.stringify on it, it will be turned into a string containing only data, functions (and a lot of other things) get removed.
I am struggling to understand how variables are referenced and stay alive in Javascript. In the following I have two types of object, a Note and an IntervalBuilder which takes a Note and creates
a second Note.
function Note() {
this.key = 1 + Math.floor( Math.random() * 13); // from 1 to 13 inclusive
this.setKey = function setKey(i) { key = i; };
this.getKey = function getKey() { return this.key; } ; // {return key} is a ReferenceError!!
}
function IntervalBuilder() {
this.setPerfectFifth = function setPerfectFifth(root) {
this.fifth = new Note();
console.log("this.fifth: " + this.fifth);
this.y = root.key;
console.log("root.key: " + root.key );
console.log("y: " + this.y );
this.fifth.setKey( this.y + 4 );
return this.fifth;
};
}
With the above I can now do this:
var x = new Note();
var ib = new IntervalBuilder();
ib.setPerfectFifth(x);
However, the instance ib now has a member named fifth! What I was hoping for was that I could assign the return value (a Note) from setPerfectFifth to a variable and let fifth vanish. How is that done?
Many thanks for any help, I find lots of this very confusing.
Gerard
Since you titled your quesion variable visibility in javascript what is basically going on is: In this.fifth = new Note(); the keyword this references the instance (the ib of var ib = new ...). So you attach your newly created Note to the instance. In JavaScript, as long as a variable can be reached starting with the global Object (window, when you think of a graph), it won't get garbage-collected away.
What you want is: var fith = new Note(), which will create a local variable which will get freed as soon as the function execution ends. Clearly, every usage of this.fifth then has to be replaced by just fith.
I do not know exactly what you want to achieve, but I think you want the following code structure:
// ==============================
// NOTE "CLASS"
// ==============================
var Note = (function () {
// Constructor
function Note() {
this._key = 1 + Math.floor( Math.random() * 13);
}
// Getter
Note.prototype.getKey = function () {
return this._key;
};
// Setter
Note.prototype.setKey = function (i) {
this._key = i;
};
return Note;
})();
// ==============================
// INTERVAL BUILDER "CLASS"
// ==============================
var IntervalBuilder = (function () {
// Constructor
function IntervalBuilder() {}
// Private members
var fifth = null,
y = 0;
// Setter
IntervalBuilder.prototype.setPerfectFifth = function (root) {
fifth = new Note();
y = root.getKey();
fifth.setKey(y + 4);
return fifth;
};
return IntervalBuilder;
})();
// ==============================
// CLIENT CODE
// ==============================
var x = new Note(),
ib = new IntervalBuilder();
ib.setPerfectFifth(x);
I have this closure :
function CFetchNextData(ofs, pag, fetchFunction) {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
}
I then create a variable this way:
var fetchInfo = CFetchNextData(0, 10, specificFetchFunction);
fetchInfo(options, myCB);
So that everytime I call fetchInfo, pagination is automatically set to the next set of data. That works great, althought
I'd like to have multiple instance of : "fetchInfo", each one having its own scope.
var A = fetchInfo; // I'd like a clone with its own scope, not a copy
var B = fetchInfo; // I'd like a clone with its own scope, not a copy
I could do:
var A = new CFetchNextData(ofs, pag, fetchFunction);
var B = new CFetchNextData(ofs, pag, fetchFunction);
But obviously I would have to setup "ofs" and "pag" each time, whereas by cloning fetchInfo, I'd have a stable pagination, set only once and for good.
Do you know how to achieve that ?
Thanks in advance
There isn't a concept of cloning a function in JavaScript. You need to call CFetchNextData (or another function) multiple times if you want to create multiple closures.
You could have CFetchNextData return a factory function instead of returning the actual function. But I'm not sure that's really an improvement.
function CFetchNextDataFactory(ofs, pag, fetchFunction) {
return function() {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
};
}
var fetchInfoFactory = CFetchNextData(0, 10, specificFetchFunction);
var A = fetchInfoFactory();
var B = fetchInfoFactory();
This may not answer all of your question but just to pitch in , you could try assigning your parameters to a default / fallback value which will allow you to avoid setting ofs and pag each declaration . Below is a prototype of what I came up with . Its using oop :
class CFetchNextData {
constructor(ofs, pag){
this.OFS = 1; //default value
this.PAG = 10; //default value
this.ofs = ofs;
this.pag = pag;
if(ofs == null || ofs == undefined){
this.ofs = this.OFS;
}
if(pag = null || pag == undefined){
this.pag = this.PAG;
}
}
fetchInfo(){
var data = this.ofs += this.pag;
return data;
}
}
var task1 = new CFetchNextData(); // Falls back to default values..
var task2 = new CFetchNextData(32,31); // Uses values from specified in args...
document.write(task1.fetchInfo() + "\n")
document.write(task2.fetchInfo())
Hope this helps...
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/
is there a way to automatically create subobjects in an assignment after construction, i.e.
var obj = {};
obj.a.b.c=13;
the above gives me a "obj.a is undefined" error
i wrote a function to do this, but wondered if there was an easier way
_setObjectProperty(obj,13,['a','b','c']);
function _setObjectProperty(obj,value,loc)
{
if(loc.length>1) {
obj[loc[0]] = obj[loc[0]] || {};
_setObjectProperty(obj[loc[0]],value,loc.splice(1));
}
else if(loc.length===1) {
obj[loc[0]]=value;
}
}
No, there's no built in way to do this in JavaScript. The only way is to create your own function like you did. If you want the convenience of the dot operator/notation you can use the following function:
var set = function(path, value, root) {
var segments = path.split('.'),
cursor = root || window,
segment,
i;
for (i = 0; i < segments.length - 1; ++i) {
segment = segments[i];
cursor = cursor[segment] = cursor[segment] || {};
}
return cursor[segments[i]] = value;
};
set("a.b.c", 2);
console.log(a.b.c) // => 2