I am working on a Logging system, and am looking forward to implement a log once method.
What I'm stuck with, is, how would I retrieve unique identifier and/or objects signature, to be used as a hash key?
My idea is:
var Log = {
messages : {},
once : function(message)
{
// get unique id and/or passed objects signature, use it as a key
var key = unique( message );
if ( !Log.messages.hasOwnProperty(key) )
{
Log.messages[key] = message;
}
}
};
I tried .toString(), but it did return simply [object Object] for a basic object, and [object <type>] for anything else. No ids, no nothing.
I also tried the toSource() function, but in Chrome it did not want to work with basic objects.
What I need, though, is for this to work with every type of object. Be it string, be it integer, be it function, I need to get that signature / id.
Maybe there is an implemention of such function somewhere around already?
Update
This is meant to do logging in a loop, but instead of logging on each iteration, log only once in order to prevent console pollution.
My particular loop is actually a game loop, and I am looking to debug some info.
Like:
for (var i = 0; i < 100; i++)
{
console.log('message');
}
// this would log 100 entries
Where:
for (var i = 0; i < 100; i++)
{
Log.once('message');
}
// would execute behind the scenes console.log() only once.
You can use JSON.stringify to get a "signature" of arbitrary values. Of course this does not save object types, and does not work for Date or Function instances; also it does not distinguish between different objects with the same values. If you want that, you will need to store a explicit reference to them (of course hindering the garbage collector from freeing them):
var loggedVals = [];
console.once = function() {
var args = [].slice.call(arguments).filter(function(arg) {
return (loggedVals.lastIndexOf(arg)==-1) && loggedVals.push(arg);
});
if (args.length)
console.log.apply(console, args);
});
If you don't want to store references to them, you will need to tag them as "logged" by adding a hidden property (just like a UID generator would do it):
… function(arg) {
return !arg.__hasbeenlogged__ &&
Object.defineProperty(arg, "__hasbeenlogged__", {value: true});
// configurable, enumarable and writable are implicitly false
} …
Notice that this solution does not work for primitive values and even fails for null and undefined - you might need to mix the two approaches.
Related
In Javascript an array [] can have properties too, because it's an object. However if you send this object to server via ajax/socket.io, it's array content stays, but its properties get lost.
For example:
var arr = ['john','peter'];
arr.a = 1;
console.log(arr.a); // 1
After sending this object via ajax, what the server sees:
console.log(arr.a); // undefined
Does anyone know why? I'm using Node.JS for server btw, thanks!
As others have mentioned, JSON.stringify() doesn't serialize array properties, even if they aren't "inherited" from proto.
If you're using jquery, a quick fix is to just use $.extend({},arr)
http://jsfiddle.net/15vn4shu/1/
If not, you can write a function to convert to an object easily:
function convertArrToObj(arr){
//assuming typeof arr === 'object', put in guards if needed
var obj = {};
for (var key in arr){
if (arr.hasOwnProperty(key))
obj[key]=arr[key];
}
return obj;
}
Personally, I'd just make an object with a property being the value of this array, add add to this parent object whatever you need. I'm not sure how feasible this is in your use case though (I'd have to see the code).
var toSend = {data:arr, customProp:'value'};
Easy, supported by all versions of js, doesn't require a framework, and no O(n) run through to do the conversion (arr is a pointer here... Which may be a gotcha, but it doesn't seem like it for your case).
I'm working on an AngularJS SPA and I'm using prototypes in order to add behavior to objects that are incoming through AJAX as JSON. Let's say I just got a timetable x from an AJAX call.
I've defined Timetable.prototype.SomeMethod = function() and I use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf in order to set the prototype of x to TimeTable.prototype. I have the polyfill in place too.
If I call x.SomeMethod() this works in IE > 9, FF, Chrome etc. However, IE 9 gives me a headache and says throws an error stating 'x does not have property or member SomeMethod'.
Debugging in IE shows me that the _proto_ of x has SomeMethod() in the list of functions, however, calling x.SomeMethod() gives the same error as described.
How can I make this work in IE9 ?
More comment than answer
The main problem with "extending" a random object retrieved from some other environment is that javascript doesn't really allow random property names, e.g. the random object may have a property name that shadows an inherited property. You might consider the following.
Use the random object purely as data and pass it to methods that access the data and do what you want, e.g.
function getName(obj) {
return obj.name;
}
So when calling methods you pass the object to a function that acts on the object and you are free to add and modify properties directly on the object.
Another is to create an instance with the methods you want and copy the object's properties to it, but then you still have the issue of not allowing random property names. But that can be mitigated by using names for inherited properties that are unlikely to clash, e.g. prefixed with _ or __ (which is a bit ugly), or use a naming convention like getSomething, setSomething, calcLength and so on.
So if obj represents data for a person, you might do:
// Setup
function Person(obj){
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
this[p] = obj[p];
}
}
}
Person.prototype.getName = function(){
return this.name;
};
// Object generated from JSON
var dataFred = {name:'fred'};
// Create a new Person based on data
var p = new Person(dataFred);
You might even use the data object to create instances from various consructors, e.g. a data object might represent multiple people, or a person and their address, which might create two related objects.
This is how I solved it at the end:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
if (!isIE9()) {
obj.__proto__ = proto;
} else {
/** IE9 fix - copy object methods from the protype to the new object **/
for (var prop in proto) {
obj[prop] = proto[prop];
}
}
return obj;
};
var isIE9 = function() {
return navigator.appVersion.indexOf("MSIE 9") > 0;
};
I'm making a class that will be recreated many times, and in order to save memory I need to thoroughly delete it. Basically I need to access its containing variable if possible.
Here's the example:
function example(){
this.id=0;
this.action=function(){alert('tost');}
this.close=function(){ delete this;}
}
var foo=new example();
My question is:
How can I get access to the foo variable from within the example function so I can remove it?
window.foo will access that global variable.
this.close=function(){ delete window.foo; }
However, I remember there is something fishy with global variables, delete and window, so you might want to do otherwise, and simply use window.foo = null; for example.
If you want to access a variable defined in another function, you'll want to read the answers to this SO question.
Since what you want is to allow the garbage collector to release that object, you need to ensure that there are no references left to the object. This can be quite tricky (i.e. impossible) because the code manipulating the object can make multiple references to it, through global and local variables, and attributes.
You could prevent direct reference to the object by creating a proxy to access it, unfortunately javascript doesn't support dynamic getters and setters (also called catch-alls) very well (on some browseres you might achieve it though, see this SO question), so you can't easily redirect all field and method (which are just fields anyway) accesses to the underlying object, especially if the underlying object has many fields added to it and removed from it dynamically (i.e. this.anewfield = anewvalue).
Here is a smiple proxy (code on jsfiddle.net):
function heavyobject(destroyself, param1, param2) {
this.id=0;
this.action=function(){alert('tost ' + param1 + "," + param2);};
this.close=function(){ destroyself(); }
}
function proxy(param1, param2) {
object = null;
// overwrites object, the only reference to
// the heavyobject, with a null value.
destroyer = function() { object = null; };
object = new heavyobject(destroyer, param1, param2);
return function(fieldname, setvalue) {
if (object != null) {
if (arguments.length == 1)
return object[fieldname];
else
object[fieldname] = setvalue;
}
};
}
var foo = proxy('a', 'b');
alert(foo("action")); // get field action
foo("afield", "avalue"); // set field afield to value avalue.
foo("action")(); // call field action
foo("close")(); // call field close
alert(foo("action")); // get field action (should be 'undefined').
It works by returning a function that when called with a single argument, gets a field on the wrapped object, and when called with two arguments sets a field. It works by making sure that the only reference to the heavyobject is the object local variable in the proxy function.
The code in heavyobject must never leak this (never return it, never return a function holding a reference to var that = this, never store it into a field of another variable), otherwise some external references may be created that would point to the heavyobject, preventing its deletion.
If heavyobject's constructor calls destroyself() from within the constructor (or from a function called by the constructor), it won't have any effect.
Another simpler proxy, that will give you an empty object on which you can add fields, read fields, and call methods. I'm pretty sure that with this one, no external reference can escape.
Code (also on jsfiddle.net):
function uniquelyReferencedObject() {
object = {};
f = function(field, value) {
if (object != null) {
if (arguments.length == 0)
object = null;
else if (arguments.length == 1)
return object[field];
else
object[field] = value;
}
};
f.destroy = function() { f(); }
f.getField = function(field) { return f(field); }
f.setField = function(field, value) { f(field, value); }
return f;
}
// Using function calls
o = uniquelyReferencedObject();
o("afield", "avalue");
alert(o("afield")); // "avalue"
o(); // destroy
alert(o("afield")); // undefined
// Using destroy, getField, setField
other = uniquelyReferencedObject();
other.setField("afield", "avalue");
alert(other.getField("afield")); // "avalue"
other.destroy();
alert(other.getField("afield")); // undefined
The truth is that you can not delete objects in Javascript.
Then you use delete operator, it accepts the property of some object only.
So, when you use delete, in general you must pass to it something like obj.p. Then you pass just a variable name actually this means 'property of global object', and delete p is the same as delete window.p. Not sure what happens internally on delete this but as a result browser just skip it.
Now, what we actually deleting with delete? We deleting a reference to object. It means object itself is still somethere in memory. To eliminate it, you must delete all references to concrete object. Everythere - from other objects, from closures, from event handlers, linked data, all of them. But object itself doest have information about all this references to it, so there is no way to delete object from object itself.
Look at this code:
var obj = <our object>;
var someAnother = {
...
myObjRef: obj
...
}
var someAnotherAnother = {
...
secondRef : obj
...
}
To eliminate obj from memory you must delete someAnother.myObjRef and someAnoterAnother.secondRef. You can do it only from the part of programm which knows about all of them.
And how we delete something at all if we can have any number of references everythere? There are some ways to solve this problem:
Make only one point in program from there this object will be referenced. In fact - there will be only one reference in our program. and Then we delete it - object will be killed by garbage collector. This is the 'proxy' way described above. This has its disadvantages (no support from language itself yet, and necessarity to change cool and nice obj.x=1 to obj.val('x',1). Also, and this is less obvious, in fact you change all references to obj to references to proxy. And proxy will always remain in memory instead of object. Depending on object size, number of objects and implementation this can give you some profit or not. Or even make things worse. For example if size of your object is near size of proxy itself - you will get no worth.
add to every place there you use an object a code which will delete reference to this object. It is more clear and simple to use, because if you call a obj.close() at some place - you already knows everything what you need to delete it. Just instead of obj.close() kill the refernce to it. In general - change this reference to something another:
var x = new obj; //now our object is created and referenced
x = null;// now our object **obj** still im memory
//but doest have a references to it
//and after some milliseconds obj is killed by GC...
//also you can do delete for properties
delete x.y; //where x an object and x.y = obj
but with this approach you must remember that references can be in very hard to understand places. For example:
function func() {
var x= new obj;// our heavy object
...
return function result() {
...some cool stuff..
}
}
the reference is stored in closure for result function and obj will remain in memory while you have a reference to result somethere.
It hard to imagine object that is heavy itself, most realistic scenario - what you have some data inside it. In this case you can add a cleanup function to object which will cleans this data. Let say you have an gigant buffer (array of numbers for example) as a property of the object, and if you want to free memory - you can just clear this buffer still having object in memory as a couple dozens of bytes. And remember to put your functions to prototype to keep instances small.
Here is a link to some very detailed information on the JavaScript delete operator.
http://perfectionkills.com/understanding-delete/
I'm working in JavaScript. I'd like to store a list of unique, unordered string values, with the following properties:
a fast way to ask 'is A in the list'?
a fast way to do 'delete A from the list if it exists in the list'
a fast way to do 'add A to the list if it is not already present'.
What I really want is a set. Any suggestions for the best way to mimic a set in JavaScript?
This question recommends using an Object, with the keys storing properties, and the values all set to true: is that a sensible way?
If you are programming in an ES6-capable environment (such as node.js, a specific browser with the ES6 capabilities you need or transpiling ES6 code for your environment), then you can use the Set object built into ES6. It has very nice capabilities and can be used as is right in your environment.
For many simple things in an ES5 environment, using an Object works very well. If obj is your object and A is a variable that has the value you want to operate on in the set, then you can do these:
Initialization code:
// create empty object
var obj = {};
// or create an object with some items already in it
var obj = {"1":true, "2":true, "3":true, "9":true};
Question 1: Is A in the list:
if (A in obj) {
// put code here
}
Question 2: Delete 'A' from the list if it's there:
delete obj[A];
Question 3: Add 'A' to the list if it wasn't already there
obj[A] = true;
For completeness, the test for whether A is in the list is a little safer with this:
if (Object.prototype.hasOwnProperty.call(obj, A))
// put code here
}
because of potential conflict between built-in methods and/or properties on the base Object like the constructor property.
Sidebar on ES6: The current working version of ECMAScript 6 or somethings called ES 2015 has a built-in Set object. It is implemented now in some browsers. Since browser availability changes over time, you can look at the line for Set in this ES6 compatibility table to see the current status for browser availability.
One advantage of the built-in Set object is that it doesn't coerce all keys to a string like the Object does so you can have both 5 and "5" as separate keys. And, you can even use Objects directly in the set without a string conversion. Here's an article that describes some of the capabilities and MDN's documentation on the Set object.
I have now written a polyfill for the ES6 set object so you could start using that now and it will automatically defer to the built-in set object if the browser supports it. This has the advantage that you're writing ES6 compatible code that will work all the way back to IE7. But, there are some downsides. The ES6 set interface takes advantage of the ES6 iterators so you can do things like for (item of mySet) and it will automatically iterate through the set for you. But, this type of language feature cannot be implemented via polyfill. You can still iterate an ES6 set without using the new ES6 languages features, but frankly without the new language features, it isn't as convenient as the other set interface I include below.
You can decide which one works best for you after looking at both. The ES6 set polyfill is here: https://github.com/jfriend00/ES6-Set.
FYI, in my own testing, I've noticed that the Firefox v29 Set implementation is not fully up-to-date on the current draft of the spec. For example, you can't chain .add() method calls like the spec describes and my polyfill supports. This is probably a matter of a specification in motion as it is not yet finalized.
Pre-Built Set objects: If you want an already built object that has methods for operating on a set that you can use in any browser, you can use a series of different pre-built objects that implement different types of sets. There is a miniSet which is small code that implements the basics of a set object. It also has a more feature rich set object and several derivations including a Dictionary (let's you store/retrieve a value for each key) and an ObjectSet (let's you keep a set of objects - either JS objects or DOM objects where you either supply the function that generates a unique key for each one or the ObjectSet will generate the key for you).
Here's a copy of the code for the miniSet (most up-to-date code is here on github).
"use strict";
//-------------------------------------------
// Simple implementation of a Set in javascript
//
// Supports any element type that can uniquely be identified
// with its string conversion (e.g. toString() operator).
// This includes strings, numbers, dates, etc...
// It does not include objects or arrays though
// one could implement a toString() operator
// on an object that would uniquely identify
// the object.
//
// Uses a javascript object to hold the Set
//
// This is a subset of the Set object designed to be smaller and faster, but
// not as extensible. This implementation should not be mixed with the Set object
// as in don't pass a miniSet to a Set constructor or vice versa. Both can exist and be
// used separately in the same project, though if you want the features of the other
// sets, then you should probably just include them and not include miniSet as it's
// really designed for someone who just wants the smallest amount of code to get
// a Set interface.
//
// s.add(key) // adds a key to the Set (if it doesn't already exist)
// s.add(key1, key2, key3) // adds multiple keys
// s.add([key1, key2, key3]) // adds multiple keys
// s.add(otherSet) // adds another Set to this Set
// s.add(arrayLikeObject) // adds anything that a subclass returns true on _isPseudoArray()
// s.remove(key) // removes a key from the Set
// s.remove(["a", "b"]); // removes all keys in the passed in array
// s.remove("a", "b", ["first", "second"]); // removes all keys specified
// s.has(key) // returns true/false if key exists in the Set
// s.isEmpty() // returns true/false for whether Set is empty
// s.keys() // returns an array of keys in the Set
// s.clear() // clears all data from the Set
// s.each(fn) // iterate over all items in the Set (return this for method chaining)
//
// All methods return the object for use in chaining except when the point
// of the method is to return a specific value (such as .keys() or .isEmpty())
//-------------------------------------------
// polyfill for Array.isArray
if(!Array.isArray) {
Array.isArray = function (vArg) {
return Object.prototype.toString.call(vArg) === "[object Array]";
};
}
function MiniSet(initialData) {
// Usage:
// new MiniSet()
// new MiniSet(1,2,3,4,5)
// new MiniSet(["1", "2", "3", "4", "5"])
// new MiniSet(otherSet)
// new MiniSet(otherSet1, otherSet2, ...)
this.data = {};
this.add.apply(this, arguments);
}
MiniSet.prototype = {
// usage:
// add(key)
// add([key1, key2, key3])
// add(otherSet)
// add(key1, [key2, key3, key4], otherSet)
// add supports the EXACT same arguments as the constructor
add: function() {
var key;
for (var i = 0; i < arguments.length; i++) {
key = arguments[i];
if (Array.isArray(key)) {
for (var j = 0; j < key.length; j++) {
this.data[key[j]] = key[j];
}
} else if (key instanceof MiniSet) {
var self = this;
key.each(function(val, key) {
self.data[key] = val;
});
} else {
// just a key, so add it
this.data[key] = key;
}
}
return this;
},
// private: to remove a single item
// does not have all the argument flexibility that remove does
_removeItem: function(key) {
delete this.data[key];
},
// usage:
// remove(key)
// remove(key1, key2, key3)
// remove([key1, key2, key3])
remove: function(key) {
// can be one or more args
// each arg can be a string key or an array of string keys
var item;
for (var j = 0; j < arguments.length; j++) {
item = arguments[j];
if (Array.isArray(item)) {
// must be an array of keys
for (var i = 0; i < item.length; i++) {
this._removeItem(item[i]);
}
} else {
this._removeItem(item);
}
}
return this;
},
// returns true/false on whether the key exists
has: function(key) {
return Object.prototype.hasOwnProperty.call(this.data, key);
},
// tells you if the Set is empty or not
isEmpty: function() {
for (var key in this.data) {
if (this.has(key)) {
return false;
}
}
return true;
},
// returns an array of all keys in the Set
// returns the original key (not the string converted form)
keys: function() {
var results = [];
this.each(function(data) {
results.push(data);
});
return results;
},
// clears the Set
clear: function() {
this.data = {};
return this;
},
// iterate over all elements in the Set until callback returns false
// myCallback(key) is the callback form
// If the callback returns false, then the iteration is stopped
// returns the Set to allow method chaining
each: function(fn) {
this.eachReturn(fn);
return this;
},
// iterate all elements until callback returns false
// myCallback(key) is the callback form
// returns false if iteration was stopped
// returns true if iteration completed
eachReturn: function(fn) {
for (var key in this.data) {
if (this.has(key)) {
if (fn.call(this, this.data[key], key) === false) {
return false;
}
}
}
return true;
}
};
MiniSet.prototype.constructor = MiniSet;
You can create an Object with no properties like
var set = Object.create(null)
which can act as a set and eliminates the need to use hasOwnProperty.
var set = Object.create(null); // create an object with no properties
if (A in set) { // 1. is A in the list
// some code
}
delete set[a]; // 2. delete A from the list if it exists in the list
set[A] = true; // 3. add A to the list if it is not already present
As of ECMAScript 6, the Set data-structure is a built-in feature. Compatibility with node.js versions can be found here.
In ES6 version of Javascript you have built in type for set (check compatibility with your browser).
var numbers = new Set([1, 2, 4]); // Set {1, 2, 4}
To add an element to the set you simply use .add(), which runs in O(1) and either adds the element to set (if it does not exist) or does nothing if it is already there. You can add element of any type there (arrays, strings, numbers)
numbers.add(4); // Set {1, 2, 4}
numbers.add(6); // Set {1, 2, 4, 6}
To check the number of elements in the set, you can simply use .size. Also runs in O(1)
numbers.size; // 4
To remove the element from the set use .delete(). It returns true if the value was there (and was removed), and false if the value did not exist. Also runs in O(1).
numbers.delete(2); // true
numbers.delete(2); // false
To check whether the element exist in a set use .has(), which returns true if the element is in the set and false otherwise. Also runs in O(1).
numbers.has(3); // false
numbers.has(1); // true
In addition to methods you wanted, there are few additional one:
numbers.clear(); would just remove all elements from the set
numbers.forEach(callback); iterating through the values of the set in insertion order
numbers.entries(); create an iterator of all the values
numbers.keys(); returns the keys of the set which is the same as numbers.values()
There is also a Weakset which allows to add only object-type values.
I have started an implementation of Sets that currently works pretty well with numbers and strings. My main focus was the difference operation, so I tried to make it as efficient as I could. Forks and code reviews are welcome!
https://github.com/mcrisc/SetJS
I just noticed that d3.js library has implementation of sets, maps and other data structures.
I can't argue about their efficiency but judging by the fact that it is a popular library it must be what you need.
The documentation is here
For convenience I copy from the link (the first 3 functions are those of interest)
d3.set([array])
Constructs a new set. If array is specified, adds the given array of string values to the returned set.
set.has(value)
Returns true if and only if this set has an entry for the specified value string.
set.add(value)
Adds the specified value string to this set.
set.remove(value)
If the set contains the specified value string, removes it and returns true. Otherwise, this method does nothing and returns false.
set.values()
Returns an array of the string values in this set. The order of the returned values is arbitrary. Can be used as a convenient way of computing the unique values for a set of strings. For example:
d3.set(["foo", "bar", "foo", "baz"]).values(); // "foo", "bar", "baz"
set.forEach(function)
Calls the specified function for each value in this set, passing the value as an argument. The this context of the function is this set. Returns undefined. The iteration order is arbitrary.
set.empty()
Returns true if and only if this set has zero values.
set.size()
Returns the number of values in this set.
Yes, that's a sensible way--that's all an object is (well, for this use-case)--a bunch of keys/values with direct access.
You'd need to check to see if it's already there before adding it, or if you just need to indicate presence, "adding" it again doesn't actually change anything, it just sets it on the object again.
I have a Javascript object that I'm trying to use as a "hashmap". The keys are always strings, so I don't think I need anything as sophisticated as what's described in this SO question. (I also don't expect the number of keys to go above about 10 so I'm not particularly concerned with lookups being O(n) vs. O(log n) etc.)
The only functionality I want that built-in Javascript objects don't seem to have, is a quick way to figure out the number of key/value pairs in the object, like what Java's Map.size returns. Of course, you could just do something like:
function getObjectSize(myObject) {
var count=0
for (var key in myObject)
count++
return count
}
but that seems kind of hacky and roundabout. Is there a "right way" to get the number of fields in the object?
There is an easier way spec'd in ECMAScript 5.
Object.keys(..) returns an array of all keys defined on the object. Length can be called on that. Try in Chrome:
Object.keys({a: 1, b: 2}).length; // 2
Note that all objects are basically key/value pairs in JavaScript, and they are also very extensible. You could extend the Object.prototype with a size method and get the count there. However, a much better solution is to create a HashMap type interface or use one of the many existing implementations out there, and define size on it. Here's one tiny implementation:
function HashMap() {}
HashMap.prototype.put = function(key, value) {
this[key] = value;
};
HashMap.prototype.get = function(key) {
if(typeof this[key] == 'undefined') {
throw new ReferenceError("key is undefined");
}
return this[key];
};
HashMap.prototype.size = function() {
var count = 0;
for(var prop in this) {
// hasOwnProperty check is important because
// we don't want to count properties on the prototype chain
// such as "get", "put", "size", or others.
if(this.hasOwnProperty(prop) {
count++;
}
}
return count;
};
Use as (example):
var map = new HashMap();
map.put(someKey, someValue);
map.size();
A correction: you need to check myObject.hasOwnProperty(key) in each iteration, because there're can be inherited attributes. For example, if you do this before loop Object.prototype.test = 'test', test will aslo be counted.
And talking about your question: you can just define a helper function, if speed doesn't matter. After all, we define helpers for trim function and other simple things. A lot of javascript is "kind of hacky and roundabout" :)
update
Failure example, as requested.
Object.prototype.test = 'test';
var x = {};
x['a'] = 1;
x['b'] = 2;
The count returned will be 3.
you could also just do myObject.length (in arrays)
nevermind, see this: JavaScript object size
That's all you can do. Clearly, JavaScript objects are not designed for this. And this will only give you the number of Enumerable properties. Try getObjectSize(Math).