I've defined the methods "checkThreshold" and "checkOtherObject" in my prototype. checkOtherObject iterates across objects listed in attribute "nextDev" and is supposed to invoke checkThreshold for each object, thus:
// Protoype
function foo(otherObject) {
// attributes
this.nextDev=otherObject; // comma-delimited list of objects
// Method that references another method
this.checkOtherObject= function() {
successorList=this.nextDev.split(",");
for (var i=0; i<successorList.length;i++) {
successorList[i]['checkThreshold']();
}
}
// Method referenced by checkOtherObject
this.checkThreshold = function () {
<Do Stuff>
}
//Instantiations
var A = new foo ("B");
var B = new foo ("");
Thus, expected behavior is that A.checkOtherObject would invoke B.checkThreshold, but when I get to that line, B.checkThreshold isn't invoked. What am I doing wrong?
The root problem: you're trying to assign value of instances in theirselves. Note: what you're doing is still wrong, otherObject is a object, but it's a String object and it's not referring to the current foo's instance (this).
this.nextDev = otherObject
Other problem is that you're calling String().checkThreshold, but not foo().checkThreshold. You can check that at your statements:
successorList = this.nextDev.split(",");
for (var i = 0; i < successorList.length; i++) {
successorList[i]['checkThreshold']();
}
As you can see you're iterating string literals. String().split returns a object with string literals back, not a list with foo()'s.
/* confirm that the successorList is a array object */
successorList instanceof Array; // true
/* confirm that the first successorList's item is a string object */
typeof successorList[0] === "string"; // true
I'm basically not sure about what's your objective. It looks like you've a extra-ordinary giant confusion between String objects and objects. Maybe you want to store nextDev out of foo(), and at nextDev you want to store a Array object containing instances of foo? Then try:
var devs = [];
function foo(string) {
devs.push(this);
}
foo.prototype = {
checkOtherObjects: function() {
var me = this;
var myIndex;
var i = 0, len = devs.length;
for (; i < len; ++i) {
if (devs[i] === me) {
myIndex = i;
break;
}
}
for (i = myIndex + 1; i < len; ++i)
devs[i].checkThresold()
}
};
To offer a bit of a better explanation - yes, you CAN use window[successorList[i]], however, that is highly, highly not recommended, and here's why:
This becomes an issue if the variables no longer in the global scope. For example, if you place the code inside of a function, including an IIFE or a document ready function, you can no longer reference your variables from the window object, unless you were to declare them with window.A, window.B, but declaring global variables like that can get so messy, especially when working with other libraries/plugins.
So...how can you work around these issues? Simple - pass the object itself as #Bergi mentioned instead of passing the string that contains the name of the variable.
Here's what the code would look like:
// Protoype
function foo(otherObject) {
this.nextDev = otherObject || [];
// Method that references another method
this.checkOtherObject = function() {
this.nextDev.forEach(function(obj, index) {
obj.checkThreshold();
});
};
// Method referenced by checkOtherObject
this.checkThreshold = function() {
// Do Stuff
console.log("Checking Threshold for: ", this);
};
}
//Instantiations
var B = new foo(null);
var A = new foo([B]); //or [B, C, D, E, ...]
A.checkOtherObject();
Related
Doing some javascript prototypical inheritance, I would like to push the arguments in my Grades constructor and do the storage manipulation and push the data inside my this.students array using my storage method, and then use the values as I please within my other methods.
But the problem is that when I console log the constructor, it does what I need it to in terms of pushing the data in the this.students array but each object comes up as undefined.
This is weird because if I run the for loop inside the Grades constructor it will work perfectly. But I would like to have a separate method to do this, inside of within my Grades constructor
Any help in pointing me in the right direction would be great! Thanks!
function Grades(studentGrades) {
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = arguments.length;
this.numRows = 0;
this.numColumns = 0;
this.init();
}
/*
* Method to initialize functions
*/
Grades.prototype.init = function() {
this.storage();
};
/*
* Method to store a list of grades in an array object
*/
Grades.prototype.storage = function() {
for(var i=0; i < this.studentGrades; i++) {
this.students.push(this.studentGrades[i]);
}
};
/*
* Method to add grades
*/
Grades.prototype.addGrades = function(numRows, numColumns, initial) {
for(this.numRows; this.numRows < this.students.length; this.numRows++ ) {
}
};
/*
* Method to display the students average
*/
Grades.prototype.display = function() {
// body...
};
var inputGrades = new Grades( [89,78,93,78], [83,67,93,98], [93,99,73,88] );
console.log(inputGrades);
I think there are some problems with your code, especially with Grades constructor :
function Grades(studentGrades) {
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = arguments.length;
this.numRows = 0;
this.numColumns = 0;
this.init();
}
You are using an array as parameter to the function but you are passing thtree parameters (arrays), I think this line:
var inputGrades = new Grades( [89,78,93,78], [83,67,93,98], [93,99,73,88] );
Should be like this:
var inputGrades = new Grades( [[89,78,93,78], [83,67,93,98], [93,99,73,88] ]);
And the following line this.studentGrades = arguments.length; is useless in the constructor and may cause problems in your code, and should be replaced with :
this.studentGrades = arguments;
Or if you pass an array of arrays like I did you can use:
this.studentGrades = studentGrades;
Your problem is inside your storage function, originating from definition.
this.studentGrades is actually defined as the length of the array, not the array itself.
If you do not store the input array or pass it on through init(inputGrades) to storage(inputGrades), then you cannot access the original input from your storage prototype.
Better: change constructor bit to:
this.students = [];
this.studentGrades = studentGrades;
And your function inside storage to:
for(var i=0; i < this.studentGrades.length; i++) {
this.students.push(this.studentGrades[i]);
}
And you should be fine I think.
UPDATE: your original function call has a variable number of arguments.
Simplest way to get to complete answer is to change argument variable to:
var inputGrades = new Grades( [[89,78,93,78], [83,67,93,98], [93,99,73,88]]);
Now you send only one argument, an array of arrays.
Alternative: change the function to
function Grades() { // so no input argument
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = Array.prototype.slice.call(arguments);
this.numRows = 0;
this.numColumns = 0;
And then you should be able to send in multiple arguments.
Having a strange issue - for some reason, accessing the parent object inside a property using the call() method only works when it is returned inside a function.
Why am I not able to access MyObject in the second example, but I can in the first?
I need to get the second working as I don't want to constantly be calling functions inside loops as it's slow and looks bad.
What I have right now:
var MyObject = {
"selectorArray": ['[id*="example"]','[class*="example"]'],
"all": function() {
return Array.prototype.slice.call(document.querySelectorAll(MyObject.selectorArray.join()));
},
"somemethod": function () {
for (var i = 0; i < MyObject.all().length; i++) {
MyObject.all()[i] // do something etc
}
}
.. I need to use all() in loops other methods also
}
What I want (faster and better looking)
var MyObject = {
"selectorArray": ['[id*="example"]','[class*="example"]'],
"all": Array.prototype.slice.call(document.querySelectorAll(MyObject.selectorArray.join())),
"somemethod": function () {
for (var i = 0; i < MyObject.all.length; i++) {
MyObject.all[i] // do something etc
}
}
.. I need to use all in loops other methods also
}
MyObject doesn't exist while the object literal is being evaluated. You can't use MyObject.anything to define the value of MyObject.all in the literal, because you're referring to a property of an object that hasn't yet been created. Instead, you can pull the value you need out of the literal:
var selectorArray = ['[id*="example"]','[class*="example"]'];
var MyObject = {
"selectorArray": selectorArray,
"all": Array.prototype.slice.call(document.querySelectorAll(selectorArray.join())),
"somemethod": function () {
for (var i = 0; i < MyObject.all.length; i++) {
MyObject.all[i] // do something etc
}
}
...
};
I am trying to add several methods to an object at once by using a for loop.
What I have is an array which has names of several events like click, load, etc. in an array and as such it will be really easy for me to insert these events to my library's object. However, I am not able to add the methods through the loop to my object.
Here's my code:
function(something) myLibrary {
if(this === window) {return new myLibrary }
this.e = document.getElementById(something);
}
var eventsArr = ['click','load','drag','drop'];
var addEventToProto = function (method) {
if(!myLibrary.hasOwnProperty(method)) {
myLibrary.prototype[method] = function (fn) { addEventListener(this.e, method, fn); };
}
};
for (i = 0; i < eventsArr.length; i += 1) {
addEventToProto(eventsArr[i]);
};
If you need more information then please leave a comment.
You should use a constructor function and manipulate the prototype property of that function instead. Object don't have an exposed prototype property, only functions have. When you create and instance, using a constructor function, then the internal [[prototype]] property of the resulting object will be set to point to the exposed prototype property of the constructor function. You can manipulate the prototype property even after instanciating an object:
function myLibraryConstructor() {
this.e = document.getElementById('someElement!');
}
var myLibrary = new myLibraryConstructor();
var eventsArr = ['click','load','drag','drop'];
var addEventToProto = function (method) {
if(!myLibrary.hasOwnProperty(method)) {
myLibraryConstructor.prototype[method] = function (fn) { addEventListener(this.e, method, fn); };
}
};
for (i = 0; i < eventsArr.length; i += 1) {
addEventToProto(eventsArr[i]);
};
I want to create a map of functions to its argument. This map will be updated dynamically. Finally all the functions will be called with their corresponding arguments.
function foo1(x) {
//do something with x
}
function foo2(x) {
//do something else with x
}
var map = {};
map[foo1] = [1,2,3]; //this array is updated dynamically in my code
map[foo2] = [4,5,6];
// I want to call foo1 and foo2 with their [1,2,3] and [4,5,6] arguments respectively.
I tried 2 approaches :
Converted foo1 to string (using toString() method) as the key for the map. Then later I get back the function from this string using Function constructor. But I am afraid if this will hit the performance.
// This works. But concerned about the performance
map[foo1.toString()] = [1,2,3];
for(i in map){
var fn = Function( 'return '+ i)();
fn(map[i]);
}
Store objects that wrap up function and their respective arguments like:
{ fn : foo1 , args : [1,2,3] }
{ fn : foo2 , args : [4,5,6] }
Here I store the references to functions instead of the entire function definition. But I have to traverse through the entire array to add more arguments.
Is there any better approach to maintain this map? What are the drawbacks in the above mentioned approaches?
UPDATE
Answer to the question "in what situation I will need this" :
I am maintaining a map from arguments to functions. I update it dynamically.
Later in my code I want to create a reverse map and call the functions with all its arguments.
For eg :
1 -> foo1
2 -> foo2
3 -> foo1,foo2
4 -> foo1
... and so on.
Then I want to create a reverse map like this :
foo1 -> [1,3,4...]
foo2 -> [2,3,...]
And finally call :
foo1( [1,3,4...])
foo2( [2,3,...])
Objects in JavaScript can only have strings as keys, so using map[foo1] is practically identical to map[foo1.toString()]. These both have problems that you haven't noticed: they discard closed-over variables, e.g.:
function makeCounter() {
var counter = 0;
return function() { return ++counter; }
}
If I have
var myCounter = makeCounter();
then myCounter.toString() will be function() { return ++counter; }, and trying to reconstitute that with the Function constructor will result in having the wrong counter reference.
Really, the best option might be to use the function's name as the property and as a value, use an object like you suggested:
var map = {};
map['foo1'] = { fn: foo1, args: [1, 2, 3] };
Then, if you want to add more arguments later, it's pretty obvious:
map['foo1'].args.push(4);
And to call them all, you might use something like this:
for(var functionName in map) {
if(!Object.prototype.hasOwnProperty.call(map, functionName)) {
continue;
}
map[functionName].fn.apply(null, map[functionName].args);
}
Until there's a native cross-browser solution for having objects as keys, you could always implement your own solution. Here's an example of what you could do. In the code below, the ObjectMap will store a generated key as a property of the object that needs to serve as a key. The property name that is used to store the key on the object is randomized to reduce possible conflicts. The map implementation can then use this property's value to retrieve the key on the object and then retrieve it's associated value.
JSPERF: http://jsperf.com/object-map
function ObjectMap() {
this.key = 0;
//you should implement a better unique id algorithm
this.mapId = '_' + Math.floor(Math.random() * 10000);
this.data = {};
}
ObjectMap.prototype = {
set: function (object, value) {
var key = ++this.key;
if (object[this.mapId]) {
return;
}
object[this.mapId] = key;
this.data[key] = value;
},
get: function (object) {
var key = object[this.mapId];
return key? this.data[key] : null;
},
remove: function (object) {
var key = object[this.mapId];
if (!key) {
return;
}
delete this.data[key];
delete object[key];
}
};
function a() {}
var map = new ObjectMap();
map.set(a, 'test');
console.log(map.get(a)); //test
In order to use objects (or functions) as keys you'll need to use Harmony (EcmaScript 6) WeakMap or Map. They're both currently experimental and both are available in Firefox. I believe WeakMap might also be available in Chrome (with the proper flag settings?).
If your platform supports WeakMap, and you choose to incorporate them, then their usage is quite straightforward:
var myWeakMap=new WeakMap();
myWeakMap.get(key [, defaultValue]);
myWeakMap.set(key, value);
myWeakMap.has(key);
myWeakMap.delete(key);
myWeakMap.clear();
More information (note the MDN references appear to be unlisted):
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/WeakMap
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Map
http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps
Also: Alternatively you can use an array of functions, then use indexOf to get the index of the function, then access the parameters in an another array with that index.
function a(){}
function b(){}
var x=[a,b].indexOf(b); //x=1
Credits to Dagg Nabbit for suggesting this in the comments under my question.
"Don't forget functions can have properties. You could always store the
functions in an array, and attach their index in the array to the
function as a propery, and look them up that way." - Dagg Nabbit
Consider the following map of args-to-callback arguments :
map :
1 -> foo1
2 -> foo1,foo2
3 -> foo2
The objective is to construct a callback-to-args map (reverse map) like this :
callbackMap:
foo1 -> [1,2]
foo2 -> [2,3]
Approach :
var allArgsPossible = [1,2,3]
// contains the list of callbacks to be called
var callbackArray = [];
//maps the callback index in callbackArray to the callback's arguments
//callbackMap[index] = args means callbackArray[index] will be called with parameter "args"
var callbackMap = {};
for( i in allArgsPossible)
{
var item = allArgsPossible[i];
var callbacks = map[ item ];
for(j in callbacks)
{
var callback = callbacks[j];
if(callback.index == undefined)
{
var index = callbackArray.length;
// adding a new property "index" to the callback
callback.index = index;
callbackMap[index] = [item];
//create a new entry in callbackArray
callbackArray.push(callback);
}
else
{
callbackMap[callback.index].push(item);
}
}
}
console.log(JSON.stringify(callbackMap));
for( i in callbackArray)
{
var callback = callbackArray[i];
//get arguments from our reverse map
var args = callbackMap[callback.index];
// Bingo !
callback(args);
}
You can get the whole picture here : http://jsfiddle.net/kyvUA/2/
One point to note here is that the callback function may already have an "index" property for some other purpose. If that is a concern, you can generate a random string and store this property on the callback with the index as the value. ( as suggested by #plalx )
Cheers !
If i have a Javascript object defined as:
function MyObj(){};
MyObj.prototype.showAlert = function(){
alert("This is an alert");
return;
};
Now a user can call it as:
var a = new MyObj();
a.showAlert();
So far so good, and one can also in the same code run another instance of this:
var b = new MyObj();
b.showAlert();
Now I want to know, how can I hold the number of instances MyObj?
is there some built-in function?
One way i have in my mind is to increment a global variable when MyObj is initialized and that will be the only way to keep track of this counter, but is there anything better than this idea?
EDIT:
Have a look at this as suggestion here:
I mean how can I make it get back to 2 instead of 3
There is nothing built-in; however, you could have your constructor function keep a count of how many times it has been called. Unfortunately, the JavaScript language provides no way to tell when an object has gone out of scope or has been garbage collected, so your counter will only go up, never down.
For example:
function MyObj() {
MyObj.numInstances = (MyObj.numInstances || 0) + 1;
}
new MyObj();
new MyObj();
MyObj.numInstances; // => 2
Of course, if you want to prevent tampering of the count then you should hide the counter via a closure and provide an accessor function to read it.
[Edit]
Per your updated question - there is no way to keep track of when instances are no longer used or "deleted" (for example by assigning null to a variable) because JavaScript provides no finalizer methods for objects.
The best you could do is create a "dispose" method which objects will call when they are no longer active (e.g. by a reference counting scheme) but this requires cooperation of the programmer - the language provides no assistance:
function MyObj() {
MyObj.numInstances = (MyObj.numInstances || 0) + 1;
}
MyObj.prototype.dispose = function() {
return MyObj.numInstances -= 1;
};
MyObj.numInstances; // => 0
var a = new MyObj();
MyObj.numInstances; // => 1
var b = new MyObj();
MyObj.numInstances; // => 2
a.dispose(); // 1 OK: lower the count.
a = null;
MyObj.numInstances; // => 1
b = null; // ERR: didn't call "dispose"!
MyObj.numInstances; // => 1
Create a static property on the MyObj constructor called say count and increment it within the constructor itself.
function MyObj() {
MyObj.count++;
}
MyObj.count = 0;
var a = new MyObj;
var b = new MyObj;
alert(MyObj.count);
This is the way you would normally do it in say Java (using a static property).
var User = (function() {
var id = 0;
return function User(name) {
this.name = name;
this.id = ++id;
}
})();
User.prototype.getName = function() {
return this.name;
}
var a = new User('Ignacio');
var b = new User('foo bar');
a
User {name: "Ignacio", id: 1}
b
User {name: "foo bar", id: 2}
Using ES6 Classes MDN syntax - we can define a static method:
The static keyword defines a static method for a class. Static methods are called without instantiating their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.
class Item {
static currentId = 0;
_id = ++Item.currentId; // Set Instance's this._id to incremented class's ID
// PS: The above line is same as:
// constructor () { this._id = ++Item.currentId; }
get id() {
return this._id; // Getter for the instance's this._id
}
}
const A = new Item(); // Create instance (Item.currentId is now 1)
const B = new Item(); // Create instance (Item.currentId is now 2)
const C = new Item(); // Create instance (Item.currentId is now 3)
console.log(A.id, B.id, C.id); // 1 2 3
console.log(`Currently at: ${ Item.currentId }`); // Currently at: 3
PS: if you don't want to log-expose the internal currentId property, make it private:
static #currentId = 0;
_id = ++Item.#currentId;
Here's an example with constructor and without the getter:
class Item {
static id = 0;
constructor () {
this.id = ++Item.id;
}
getID() {
console.log(this.id);
}
}
const A = new Item(); // Create instance (Item.id is now 1)
const B = new Item(); // Create instance (Item.id is now 2)
const C = new Item(); // Create instance (Item.id is now 3)
A.getID(); B.getID(); C.getID(); // 1; 2; 3
console.log(`Currently at: ${ Item.id }`); // Currently at: 3
what about such method?
var Greeter = (function ()
{
var numInstances;
function Greeter(message)
{
numInstances = (numInstances || 0) + 1;
this.greeting = message;
}
Greeter.prototype.greet = function ()
{
return "Hello, " + this.greeting;
};
Greeter.prototype.getCounter = function ()
{
return numInstances;
};
return Greeter;
})();
var greeter = new Greeter("world");
greeter.greet();
greeter.getCounter();
var newgreeter = new Greeter("new world");
newgreeter.greet();
newgreeter.getCounter();
greeter.getCounter();
Keeping a global count variable and incrementing every time is an option. Another option is to call counter method after each instance creation by hand (the worst thing I could imagine). But there is another better solution.
Every time we create an instance, the constructor function is being called. The problem is the constructor function is being created for each instance, but we can have a count property inside __proto__ which can be the same for each instance.
function MyObj(){
MyObj.prototype.addCount();
};
MyObj.prototype.count = 0;
MyObj.prototype.addCount = function() {
this.count++;
};
var a = new MyObj();
var b = new MyObj();
This is our a and b variables after all:
Eventually, JS is going to have built-in proxy capability, which will have low-level access to all kinds of things which happen in the background, which will never be exposed to front-end developers (except through the proxy -- think magic-methods in languages like PHP).
At that time, writing a destructor method on your object, which decrements the counter might be entirely trivial, as long as support for destruction/garbage-collection as a trigger is 100% guaranteed across platforms.
The only way to currently, reliably do it might be something like creating an enclosed registry of all created instances, and then manually destructing them (otherwise, they will NEVER be garbage-collected).
var Obj = (function () {
var stack = [],
removeFromStack = function (obj) {
stack.forEach(function (o, i, arr) {
if (obj === o) { arr.splice(i, 1); }
makeObj.count -= 1;
});
};
function makeObj (name) {
this.sayName = function () { console.log("My name is " + this.name); }
this.name = name;
this.explode = function () { removeFromStack(this); };
stack.push(this);
makeObj.count += 1;
}
makeObj.checkInstances = function () { return stack.length; };
makeObj.count = 0;
return makeObj;
}());
// usage:
var a = new Obj("Dave"),
b = new Obj("Bob"),
c = new Obj("Doug");
Obj.count; // 3
// "Dave? Dave's not here, man..."
a.explode();
Obj.count; // 2
a = null; // not 100% necessary, if you're never going to call 'a', ever again
// but you MUST call explode if you ever want it to leave the page's memory
// the horrors of memory-management, all over again
Will this pattern do what you want it to do?
As long as:
you don't turn a into something else
you don't overwrite its explode method
you don't mess with Obj in any way
you don't expect any prototype method to have access to any of the internal variables
...then yes, this method will work just fine for having the counter work properly.
You could even write a general method called recycle, which calls the explode method of any object you pass it (as long as its constructor, or factory, supported such a thing).
function recycle (obj) {
var key;
obj.explode();
for (key in obj) { if (obj.hasOwnProperty(key)) { delete obj[key]; } }
if (obj.__proto__) { obj.__proto__ = null; }
}
Note - this won't actually get rid of the object.
You'll just have removed it from the closure, and removed all methods/properties it once had.
So now it's an empty husk, which you could reuse, expressly set to null after recycling its parts, or let it be collected and forget about it, knowing that you removed necessary references.
Was this useful?
Probably not.
The only time I really see this as being of use would be in a game where your character might only be allowed to fire 3 bullets at a time, and he can't shoot a 4th until the 1st one on screen hits someone or goes off the edge (this is how, say, Contra worked, in the day).
You could also just shift a "disappeared" bullet off the stack, and reuse that bullet for any player/enemy by resetting its trajectory, resetting appropriate flags, and pushing it back onto the stack.
But again, until proxies allow us to define "magic" constructor/destructor methods, which are honoured at a low-level, this is only useful if you're going to micromanage the creation and destruction of all of your own objects (really not a good idea).
My solution is creating an object store instance count and a function to increase them in prototype.
function Person() {
this.countInst();
}
Person.prototype = {
constructor: Person,
static: {
count: 0
},
countInst: function() {
this.static.count += 1;
}
};
var i;
for (i = 0; i < 10; i++) {
var p = new Person();
document.write('Instance count: ');
document.write(p.static.count);
document.write('<br />');
}
Here is my plunker: https://plnkr.co/edit/hPtIR2MQnV08L9o1oyY9?p=preview
class Patient{
constructor(name,age,id){
Object.assign(this,{name, age, id});
}
static patientList = []; // declare a static variable
static addPatient(obj){
this.patientList.push(...obj); // push to array
return this.patientList.length; // find the array length to get the number of objects
}
}
let p1 = new Patient('shreyas',20, 1);
let p2 = new Patient('jack',25, 2);
let p3 = new Patient('smith',22, 3);
let patientCount = Patient.addPatient([p1,p2,p3]); // call static method to update the count value with the newly created object
console.log(Patient.patientList);
console.log(patientCount);