Javascript - dumping all global variables - javascript

Is there a way in Javascript to get a list or dump the contents of all global variables declared by Javascript/jQuery script on a page? I am particularly interested in arrays. If I can get the array names, it will be enough to me. Seeing its values is a bonus.

Object.keys( window );
This will give you an Array of all enumerable properties of the window object, (which are global variables).
For older browsers, include the compatibility patch from MDN.
To see its values, then clearly you'll just want a typical enumerator, like for-in.
You should note that I mentioned that these methods will only give you enumerable properties. Typically those will be ones that are not built-in by the environment.
It is possible to add non-enumerable properties in ES5 supported browsers. These will not be included in Object.keys, or when using a for-in statement.
As noted by #Raynos, you can Object.getOwnPropertyNames( window ) for non-enumerables. I didn't know that. Thanks #Raynos!
So to see the values that include enumerables, you'd want to do this:
var keys = Object.getOwnPropertyNames( window ),
value;
for( var i = 0; i < keys.length; ++i ) {
value = window[ keys[ i ] ];
console.log( value );
}

The following function only dumps global variables that have been added to the window object:
(function(){
//noprotect <- this comment prevents jsbin interference
var windowProps = function() {
// debugger;
var result = {};
for (var key in window) {
if (Object.prototype.hasOwnProperty.call(window, key)) {
if ((key|0) !== parseInt(key,10)) {
result[key] = 1;
}
}
}
window.usedVars = result;
};
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'about:blank';
document.body.appendChild(iframe);
var fwin = iframe.contentWindow;
var fdoc = fwin.document;
fdoc.open('text/html','replace');
fdoc.write('<!DOCTYPE html><body><script>window.onload = ' + windowProps.toString() + '<\u002Fscript>');
fdoc.close();
var button = document.createElement('input');
button.type = 'button';
button.value = 'go';
document.body.appendChild(button);
button.onclick = function() {
var clean = fwin.usedVars;
windowProps();
var dirty = window.usedVars;
for (var key in clean) {
delete dirty[key];
}
for (var variable in dirty) {
var div = document.createElement('div');
div.textContent = variable;
document.body.appendChild(div);
}
document.body.removeChild(button);
document.body.removeChild(iframe);
};
})();
It works by using an iframe to get a clean list of global window variables, then comparing that with the list of global variables in the current window. It uses a button because the iframe runs asynchronously. The code uses a global variable because that makes the code easier to understand.
You can see it working here or here, although note that these examples show many global variables "leaked" by jsbin itself (different depending on which link you use).

Since all global variables are properties of the window object, you can get them using:
for(var key in window) { // all properties
if(Array.isArray(window[key])) { // only arrays
console.log(key, window[key]); // log key + value
}
}
Since all default/inherited properties are not plain arrays (mostly host objects or functions), the Array.isArray check is sufficient.

To get "globals" object you can use this function:
function globals() { return this; }
Here is the test:
http://jsfiddle.net/EERuf/

window is the global object in a browser, and you can use a for..in loop to loop through its properties:
if(!Array.isArray) {
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
for(var x in window) {
if(Array.isArray(window[x])) {
console.log('Found array ' + x + ' in ' + window + ', it has the value ' + window[x] + '!');
}
}

You can use npm package called get-globals. It compares properties of window with fresh-created iframe to print only variables declared by dev(s), not browser vendor.

Greasymonkey script to get leaked globals
// ==UserScript==
// #name SCI
// #namespace ns
// #version 1
// #grant none
// #run-at document-start
// ==/UserScript==
console.log('SCI loaded');
var SCI = window.SCI = {
defprops: [],
collect: function(){
var wprops = [];
for(var prop in window){
wprops.push(prop);
}
return wprops;
},
collectDef: function(){
this.defprops = this.collect();
},
diff: function(){
var def = this.defprops,
cur = this.collect();
var dif = [];
for(var i = 0; i < cur.length; i++){
var p = cur[i];
if(def.indexOf(p) === -1){
dif.push(p);
}
}
return dif;
},
diffObj: function(){
var diff = this.diff();
var dobj = {};
for (var i = 0; i < diff.length; i++){
var p = diff[i];
dobj[p]=window[p];
}
return dobj;
}
};
SCI.collectDef();
To use run in console SCI.diff() to get list of names or SCI.diffObj() to get object with variables

Here’s a simple, more modern snippet that logs an object with the globals and their values (rather than just the global variable names), which is usually what I’m looking for when debugging:
(function () {
const iframe = document.createElement('iframe')
iframe.setAttribute('hidden', '')
iframe.src = 'about:blank'
iframe.onload = function () {
// Iterate through the properties of the current `window` and reduce the output
// to only properties that are not in the iframe’s `window`.
console.debug(Object.entries(window).reduce((reducedObj, [property, value]) => {
// Check if the property does not exist in the iframe and if so, add it to
// the output object.
if (! (property in iframe.contentWindow))
reducedObj[property] = value
return reducedObj
}, {})))
// Clean up the iframe by removing it from the DOM.
iframe.remove()
}
// Append the iframe to the DOM to kick off loading.
document.body.append(iframe)
})()
Tip: You can also swap 'about:blank' with window.location to get only the globals set since the page was first loaded.
This uses an iframe to determine which properties to ignore like robocat’s answer, but it’s based on David Walsh’s solution.

Related

does javascript have something equivalent to Python's `id` function? [duplicate]

I need to do some experiment and I need to know some kind of unique identifier for objects in javascript, so I can see if they are the same. I don't want to use equality operators, I need something like the id() function in python.
Does something like this exist ?
Update My original answer below was written 6 years ago in a style befitting the times and my understanding. In response to some conversation in the comments, a more modern approach to this is as follows:
(function() {
if ( typeof Object.id != "undefined" ) return;
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
console.log(Object.id(function() {}));
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
If you have archaic browser requirements, check here for browser compatibility for Object.defineProperty.
The original answer is kept below (instead of just in the change history) because I think the comparison is valuable.
You can give the following a spin. This also gives you the option to explicitly set an object's ID in its constructor or elsewhere.
(function() {
if ( typeof Object.prototype.uniqueId == "undefined" ) {
var id = 0;
Object.prototype.uniqueId = function() {
if ( typeof this.__uniqueid == "undefined" ) {
this.__uniqueid = ++id;
}
return this.__uniqueid;
};
}
})();
var obj1 = {};
var obj2 = new Object();
console.log(obj1.uniqueId());
console.log(obj2.uniqueId());
console.log([].uniqueId());
console.log({}.uniqueId());
console.log(/./.uniqueId());
console.log((function() {}).uniqueId());
Take care to make sure that whatever member you use to internally store the unique ID doesn't collide with another automatically created member name.
So far as my observation goes, any answer posted here can have unexpected side effects.
In ES2015-compatible enviroment, you can avoid any side effects by using WeakMap.
const id = (() => {
let currentId = 0;
const map = new WeakMap();
return (object) => {
if (!map.has(object)) {
map.set(object, ++currentId);
}
return map.get(object);
};
})();
id({}); //=> 1
Latest browsers provide a cleaner method for extending Object.prototype. This code will make the property hidden from property enumeration (for p in o)
For the browsers that implement defineProperty, you can implement uniqueId property like this:
(function() {
var id_counter = 1;
Object.defineProperty(Object.prototype, "__uniqueId", {
writable: true
});
Object.defineProperty(Object.prototype, "uniqueId", {
get: function() {
if (this.__uniqueId == undefined)
this.__uniqueId = id_counter++;
return this.__uniqueId;
}
});
}());
For details, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Actually, you don't need to modify the object prototype and add a function there. The following should work well for your purpose.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
For browsers implementing the Object.defineProperty() method, the code below generates and returns a function that you can bind to any object you own.
This approach has the advantage of not extending Object.prototype.
The code works by checking if the given object has a __objectID__ property, and by defining it as a hidden (non-enumerable) read-only property if not.
So it is safe against any attempt to change or redefine the read-only obj.__objectID__ property after it has been defined, and consistently throws a nice error instead of silently fail.
Finally, in the quite extreme case where some other code would already have defined __objectID__ on a given object, this value would simply be returned.
var getObjectID = (function () {
var id = 0; // Private ID counter
return function (obj) {
if(obj.hasOwnProperty("__objectID__")) {
return obj.__objectID__;
} else {
++id;
Object.defineProperty(obj, "__objectID__", {
/*
* Explicitly sets these two attribute values to false,
* although they are false by default.
*/
"configurable" : false,
"enumerable" : false,
/*
* This closure guarantees that different objects
* will not share the same id variable.
*/
"get" : (function (__objectID__) {
return function () { return __objectID__; };
})(id),
"set" : function () {
throw new Error("Sorry, but 'obj.__objectID__' is read-only!");
}
});
return obj.__objectID__;
}
};
})();
Typescript version of #justin answer, ES6 compatible, using Symbols to prevent any key collision and added into the global Object.id for convenience. Just copy paste the code below, or put it into an ObjecId.ts file you will import.
(enableObjectID)();
declare global {
interface ObjectConstructor {
id: (object: any) => number;
}
}
const uniqueId: symbol = Symbol('The unique id of an object');
export function enableObjectID(): void {
if (typeof Object['id'] !== 'undefined') {
return;
}
let id: number = 0;
Object['id'] = (object: any) => {
const hasUniqueId: boolean = !!object[uniqueId];
if (!hasUniqueId) {
object[uniqueId] = ++id;
}
return object[uniqueId];
};
}
Example of usage:
console.log(Object.id(myObject));
jQuery code uses it's own data() method as such id.
var id = $.data(object);
At the backstage method data creates a very special field in object called "jQuery" + now() put there next id of a stream of unique ids like
id = elem[ expando ] = ++uuid;
I'd suggest you use the same method as John Resig obviously knows all there is about JavaScript and his method is based on all that knowledge.
For the purpose of comparing two objects, the simplest way to do this would be to add a unique property to one of the objects at the time you need to compare the objects, check if the property exists in the other and then remove it again. This saves overriding prototypes.
function isSameObject(objectA, objectB) {
unique_ref = "unique_id_" + performance.now();
objectA[unique_ref] = true;
isSame = objectB.hasOwnProperty(unique_ref);
delete objectA[unique_ref];
return isSame;
}
object1 = {something:true};
object2 = {something:true};
object3 = object1;
console.log(isSameObject(object1, object2)); //false
console.log(isSameObject(object1, object3)); //true
I faced the same problem and here's the solution I implemented with ES6
code
let id = 0; // This is a kind of global variable accessible for every instance
class Animal {
constructor(name){
this.name = name;
this.id = id++;
}
foo(){}
// Executes some cool stuff
}
cat = new Animal("Catty");
console.log(cat.id) // 1
I've used code like this, which will cause Objects to stringify with unique strings:
Object.prototype.__defineGetter__('__id__', function () {
var gid = 0;
return function(){
var id = gid++;
this.__proto__ = {
__proto__: this.__proto__,
get __id__(){ return id }
};
return id;
}
}.call() );
Object.prototype.toString = function () {
return '[Object ' + this.__id__ + ']';
};
the __proto__ bits are to keep the __id__ getter from showing up in the object. this has been only tested in firefox.
Notwithstanding the advice not to modify Object.prototype, this can still be really useful for testing, within a limited scope. The author of the accepted answer changed it, but is still setting Object.id, which doesn't make sense to me. Here's a snippet that does the job:
// Generates a unique, read-only id for an object.
// The _uid is generated for the object the first time it's accessed.
(function() {
var id = 0;
Object.defineProperty(Object.prototype, '_uid', {
// The prototype getter sets up a property on the instance. Because
// the new instance-prop masks this one, we know this will only ever
// be called at most once for any given object.
get: function () {
Object.defineProperty(this, '_uid', {
value: id++,
writable: false,
enumerable: false,
});
return this._uid;
},
enumerable: false,
});
})();
function assert(p) { if (!p) throw Error('Not!'); }
var obj = {};
assert(obj._uid == 0);
assert({}._uid == 1);
assert([]._uid == 2);
assert(obj._uid == 0); // still
This one will calculate a HashCode for each object, optimized for string, number and virtually anything that has a getHashCode function. For the rest it assigns a new reference number.
(function() {
var __gRefID = 0;
window.getHashCode = function(ref)
{
if (ref == null) { throw Error("Unable to calculate HashCode on a null reference"); }
// already cached reference id
if (ref.hasOwnProperty("__refID")) { return ref["__refID"]; }
// numbers are already hashcodes
if (typeof ref === "number") { return ref; }
// strings are immutable, so we need to calculate this every time
if (typeof ref === "string")
{
var hash = 0, i, chr;
for (i = 0; i < ref.length; i++) {
chr = ref.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash;
}
// virtual call
if (typeof ref.getHashCode === "function") { return ref.getHashCode(); }
// generate and return a new reference id
return (ref["__refID"] = "ref" + __gRefID++);
}
})();
If you came here because you deal with class instances like me you can use static vars/methods to reference instances by a custom unique id:
class Person {
constructor( name ) {
this.name = name;
this.id = Person.ix++;
Person.stack[ this.id ] = this;
}
}
Person.ix = 0;
Person.stack = {};
Person.byId = id => Person.stack[ id ];
let store = {};
store[ new Person( "joe" ).id ] = true;
store[ new Person( "tim" ).id ] = true;
for( let id in store ) {
console.log( Person.byId( id ).name );
}
Here's a variant of Justin Johnson's answer that provides a scalability benefit when you are creating billions of objects for which you want the ID.
Specifically, rather than solely using a 1-up counter (that might overflow the representational limits of Number, and can't be cycled without risking reusing an ID), we register the object and its newly generated ID with a FinalizationRegistry, such that, at some point after the object is garbage collected, the ID is returned to a freelist for reuse by a newly created object (Python's id function can also return the same ID for multiple objects, so long as the existence of the two objects does not overlap in time).
Limitations:
It only works on objects, not JS primitives (this is somewhat reasonable; unlike Python, where everything is an object, JS primitives typically aren't, and the id function logically only works on objects, since primitives need not "exist" in any reasonably identifiable way).
If the code creates (without discarding) billions of objects, asks for their IDs, then releases them all at once and never asks for an ID again, the recovered IDs in the freelist constitute a memory leak of sorts. Hopefully the JS optimizer stores them efficiently, so the cost remains a small fraction of what the objects themselves cost, but it's still a cost. In cases where objects with IDs are regularly created and destroyed, the wasted memory is roughly tied to the maximum number of such ID-ed objects in existence at any given point in time.
If those limitations aren't a problem though, this works fairly well. I modified the testing code a bit to hand control back to the event loop (and hopefully the garbage collector) now and again while creating 10M garbage objects to ID, and on my browser, nearly half the object IDs get reclaimed for reuse; the final loop making five objects and IDing them produces IDs just above 1M, when over 2M objects had IDs generated at some point. In a realistic scenario with meaningful code executing and real async usage I'd expect better results simply because there would be more opportunities for the finalization registry to perform cleanup.
async function sleep(ms) {
await _sleep(ms);
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(function() {
if ( typeof Object.id != "undefined" ) return;
var freelist = []; // Stores previously used IDs for reuse when an object with
// an ID is garbage collected, so creating and dropping billions
// of objects doesn't consume all available IDs
const registry = new FinalizationRegistry((freeid) => {
freelist.push(freeid);
});
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: freelist.length ? freelist.pop() : ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
registry.register(o, o.__uniqueid); // Sometime after o is collected, its ID
// will be reclaimed for use by a new object
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
var idsum = 0; // So we do something real to prevent optimizing out code
// Make a ton of temporary objects with IDs, handing control back to the event loop
// every once in a while to (hopefully) see some IDs returned to the pool
for (var i = 0; i < 1000000; ++i) {
idsum += Object.id({c: i});
}
sleep(10).then(() => {
console.log(Object.id(function() { console.log("Hey"); }));
for (var i = 1000000; i < 2000000; ++i) {
idsum += Object.id({c: i});
}
console.log(Object.id(function() { console.log("There"); }));
sleep(10).then(() => {
for (var i = 0; i < 5; ++i) {
console.log(Object.id([i]));
}
console.log(idsum);
});
});
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`

Javascript, callback when object's attributes change?

Functions in javascript are objects:
var x = function(){};
x.y = 1;
console.log(x.y); //Prints 1
Is there any way to call a function when y changes?
My reason for doing this is that I'm trying to override jquery's "$" function so that I can benchmark performance. It works fine when the JS runs $('mySelector'). However, plugins that are created using $.fn.myPlugin will change the attributes in the object I overrided, rather than the original.
I believe what you're looking for are JavaScript setters. Be careful to study browser support tough.
The following example is from the MDN documentation. First, when you create your own objects:
var o = {a: 7,
get b() {return this.a + 1;},
set c(x) {this.a = x / 2}};
Second, when you augment an existing prototype:
var d = Date.prototype;
d.__defineGetter__("year", function() { return this.getFullYear(); });
d.__defineSetter__("year", function(y) { this.setFullYear(y); });
There is no way to do this that will work in all browsers. Getters and setters are nice, but are completely useless when you have to support IE6 or IE7.
The approach I settled on used bandi's defineGetter suggestion, but looped through the attributes of the original object.
getAttr = function(att) {
return function() { return eval("originalObject."+att) };
}
//Delegate all attribute access to original object
for (var att in originalObject) {
overRiddenObject.__defineGetter__(att.toString(), getAttr(att));
}
And here is the full snippet that I can stick in my code to profile jQuery selectors:
var original$ = $;
$ = function() {
//Ignore document.ready function calls
if (typeof(arguments)[0] === 'function') {
return original$.apply( this, arguments );
}
t1 = new Date().getTime();
// Run each selector 100 times since we can only time it to the millisecond
for (var i=0; i<100; i++)
x = original$.apply( this, arguments );
t = new Date().getTime()-t1;
// Print selector name, time taken and number of elements matched
console.log(arguments," "+t+"ms"+" ("+x.length+" element(s))");
return x;
};
getAttr = function(att) {
return function() { return eval("original$."+att) };
}
//Delegate all attribute access to original object
for (var att in original$) {
$.__defineGetter__(att.toString(), getAttr(att));
}
It could probably be done with better style using closure's, but it works.
Also it will only work on jQuery plugins when you remove the "$" from (function($) {...))(jQUery).
This could be worked around by rewriting this to use .extend and override methods that are called during selection - something I'm looking into to make this pluggable.

How to detect creation of new global variables?

I want watch for the creation of new global variables in Javascript so that, anytime a global variable is created, an event is fired.
I've heard of the watch() function but that is only for watching for specific variable names. I want a catchall.
If you already know which names pollute your global namespace (see Intercepting global variable definition in javascript), you can use this trick to figure out when does it actually happen:
window.__defineSetter__('someGlobalVar', function() {
debugger;
});
Be sure to have your developer tools open when you run this.
Obviously works only if your browser supports __defineSetter__ but that's true for modern browsers. Also, don't forget to remove your debug code after you've finished.
Found here.
I don't know how to make this work "on demand" as soon as a var is created, but I can suggest a polling approach. In a browser window, all global become a member of the global "window" object. (Because technically, "window" is the "global object"). So you could do something like the following:
1) enumerate all the properties on a window
window.proplist = window.proplist || {};
for (propname in window) {
if (propname !== "proplist") {
window.proplist[propname] = true;
}
}
2) Set a timer to periodically "poll" window for new properties
setInterval(onTimer, 1000);
3) Wake up on the timer callback and look for new props
function onTimer() {
if (!window.proplist) {
return;
}
for (propname in window) {
if (!(window.proplist[propname])) {
window.proplist[propname] = true;
onGlobalVarCreated(propname);
}
}
}
Afaik, .watch() is only SpiderMonkey (Firefox).
I played around with a polling function, I finally came up with this:
var mywatch = (function() {
var last = {
count: 0,
elems: {}
};
return function _REP(cb) {
var curr = {
count: 0,
elems: {}
},
diff = {};
for(var prop in window) {
if( window.hasOwnProperty(prop) ) {
curr.elems[prop] = window[prop]; curr.count++;
}
}
if( curr.count > last.count ) {
for(var comp in curr.elems) {
if( !(comp in last.elems) ) {
diff[comp] = curr.elems[comp];
}
}
last.count = curr.count;
last.elems = curr.elems;
if(typeof cb === 'function')
cb.apply(null, [diff]);
}
setTimeout(function() {
_REP(cb);
}, 400);
};
}());
And then use it like:
mywatch(function(diff) {
console.log('NEW GLOBAL(s): ', diff);
});
Be aware that this only handles new globals. But you can easily extend this for the case last.count > curr.count. That would indicate that global variables were deleted.
You cant have an event fired when some script does var v = 10, but as selbie said, you can poll the window object... I meant to suggest the same, but he beat me to it. Here's my other example... you count how many window objects are there, and execute GlobalVarCreated() function:
var number_of_globals = 0; //last known globals count
var interval = window.setInterval(function(){
var new_globals_count = 0; //we count again
for(var i in window) new_globals_count++; //actual counting
if(number_of_globals == 0) number_of_globals = new_globals_count; //first time we initialize old value
else{
var number_of_new_globals = new_globals_count - number_of_globals; //new - old
if(number_of_new_globals > 0){ //if the number is higher then 0 then we have some vars
number_of_globals = new_globals_count;
for(var i = 0; i<number_of_new_globals; i++) GlobalVarCreated(); //if we have 2 new vars we call handler 2 times...
}
}
},300); //each 300ms check is run
//Other functions
function GlobalVarCreated(){}
function StopInterval(){window.clearInterval(interval);}
You can load up that code in Chrome or FF console only change: function GlobalVarCreated(){console.log("NEW VAR CREATED");} and test it:
var a = 10
b = 10
String NEW VAR CREATED is displayed 2 times.

Assigning values to an element

I was wondering if it was possible to assign values to an element object. In this case, I wish to assign the returns from the setTimeout() function to an object within an element object.
For example:
var elem = document.getElementById('target');
elem.timeouts = new Object();
elem.timeouts.sometimeout = setTimeout('...', 1000);
So I could then do:
clearTimeout(elem.timeouts.sometimeout);
I know this might seem bad practice etc, but is it possible or will it cause browsers to catch fire and turn on their user etc.
Thanks.
DOM elements are Host objects (aka non-native) and as such they can do almost anything they want. It's not guaranteed that your expandos will work. In particular IE used to have problems with them. It's highly recommended to read this article:
What’s wrong with extending the DOM by #kangax
(it is from one of the Prototype.js developers who experienced the drawbacks of such bad habits. They've rewritten the whole library just to save themselfs from more headaches)
Now if you add uniqueID to elements in non-IE browsers (IE has it by default) and then your data function becomes a simple lookup ~ O(1). The real information will be stored in a closure.
It's 2-4x faster than jQuery.data (test)
data(elem, "key", "value");
1.) Hash table
var data = (function () {
var storage = {};
var counter = 1;
return function (el, key, value) {
var uid = el.uniqueID || (el.uniqueID = counter++);
if (typeof value != "undefined") {
storage[uid] || (storage[uid] = {});
storage[uid][key] = value; // set
} else if (storage[uid]) {
return storage[uid][key]; // get
}
};
})();
2.) Array
If you want to avoid expandos all together you can use an array to hold elements (but it's slower)
var data = (function () {
var elements = [];
var storage = [];
return function (el, key, value) {
var i = elements.indexOf(el);
if (typeof value != "undefined") {
if (i == -1) {
i = elements.length;
elements[i] = el;
storage[i] = {};
}
storage[i][key] = value; // set
} else if (storage[i]) {
return storage[i][key]; // get
}
};
})();
Array.prototype.indexOf (fallback)
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (item) {
var len = this.length >>> 0;
for (var i = 0; i < len; i++) {
if (this[i] === item) {
return i;
}
}
return -1;
};
}
You're welcome! :)
It's possible, DOM elements retrieved by JS, are JS variables :) ..BTW it's not a common practice do that stuff in that way. I think the answer of #galambalazs is more deep and complete ;)
if you are using jquery, you can story it in the "data"
http://api.jquery.com/jQuery.data/
No, actually that should work just fine. From what I understand, adding that return to the object should be fine. It's better than storing it in a global container IMO.

unique object identifier in javascript

I need to do some experiment and I need to know some kind of unique identifier for objects in javascript, so I can see if they are the same. I don't want to use equality operators, I need something like the id() function in python.
Does something like this exist ?
Update My original answer below was written 6 years ago in a style befitting the times and my understanding. In response to some conversation in the comments, a more modern approach to this is as follows:
(function() {
if ( typeof Object.id != "undefined" ) return;
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
console.log(Object.id(function() {}));
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
If you have archaic browser requirements, check here for browser compatibility for Object.defineProperty.
The original answer is kept below (instead of just in the change history) because I think the comparison is valuable.
You can give the following a spin. This also gives you the option to explicitly set an object's ID in its constructor or elsewhere.
(function() {
if ( typeof Object.prototype.uniqueId == "undefined" ) {
var id = 0;
Object.prototype.uniqueId = function() {
if ( typeof this.__uniqueid == "undefined" ) {
this.__uniqueid = ++id;
}
return this.__uniqueid;
};
}
})();
var obj1 = {};
var obj2 = new Object();
console.log(obj1.uniqueId());
console.log(obj2.uniqueId());
console.log([].uniqueId());
console.log({}.uniqueId());
console.log(/./.uniqueId());
console.log((function() {}).uniqueId());
Take care to make sure that whatever member you use to internally store the unique ID doesn't collide with another automatically created member name.
So far as my observation goes, any answer posted here can have unexpected side effects.
In ES2015-compatible enviroment, you can avoid any side effects by using WeakMap.
const id = (() => {
let currentId = 0;
const map = new WeakMap();
return (object) => {
if (!map.has(object)) {
map.set(object, ++currentId);
}
return map.get(object);
};
})();
id({}); //=> 1
Latest browsers provide a cleaner method for extending Object.prototype. This code will make the property hidden from property enumeration (for p in o)
For the browsers that implement defineProperty, you can implement uniqueId property like this:
(function() {
var id_counter = 1;
Object.defineProperty(Object.prototype, "__uniqueId", {
writable: true
});
Object.defineProperty(Object.prototype, "uniqueId", {
get: function() {
if (this.__uniqueId == undefined)
this.__uniqueId = id_counter++;
return this.__uniqueId;
}
});
}());
For details, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Actually, you don't need to modify the object prototype and add a function there. The following should work well for your purpose.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
For browsers implementing the Object.defineProperty() method, the code below generates and returns a function that you can bind to any object you own.
This approach has the advantage of not extending Object.prototype.
The code works by checking if the given object has a __objectID__ property, and by defining it as a hidden (non-enumerable) read-only property if not.
So it is safe against any attempt to change or redefine the read-only obj.__objectID__ property after it has been defined, and consistently throws a nice error instead of silently fail.
Finally, in the quite extreme case where some other code would already have defined __objectID__ on a given object, this value would simply be returned.
var getObjectID = (function () {
var id = 0; // Private ID counter
return function (obj) {
if(obj.hasOwnProperty("__objectID__")) {
return obj.__objectID__;
} else {
++id;
Object.defineProperty(obj, "__objectID__", {
/*
* Explicitly sets these two attribute values to false,
* although they are false by default.
*/
"configurable" : false,
"enumerable" : false,
/*
* This closure guarantees that different objects
* will not share the same id variable.
*/
"get" : (function (__objectID__) {
return function () { return __objectID__; };
})(id),
"set" : function () {
throw new Error("Sorry, but 'obj.__objectID__' is read-only!");
}
});
return obj.__objectID__;
}
};
})();
Typescript version of #justin answer, ES6 compatible, using Symbols to prevent any key collision and added into the global Object.id for convenience. Just copy paste the code below, or put it into an ObjecId.ts file you will import.
(enableObjectID)();
declare global {
interface ObjectConstructor {
id: (object: any) => number;
}
}
const uniqueId: symbol = Symbol('The unique id of an object');
export function enableObjectID(): void {
if (typeof Object['id'] !== 'undefined') {
return;
}
let id: number = 0;
Object['id'] = (object: any) => {
const hasUniqueId: boolean = !!object[uniqueId];
if (!hasUniqueId) {
object[uniqueId] = ++id;
}
return object[uniqueId];
};
}
Example of usage:
console.log(Object.id(myObject));
jQuery code uses it's own data() method as such id.
var id = $.data(object);
At the backstage method data creates a very special field in object called "jQuery" + now() put there next id of a stream of unique ids like
id = elem[ expando ] = ++uuid;
I'd suggest you use the same method as John Resig obviously knows all there is about JavaScript and his method is based on all that knowledge.
For the purpose of comparing two objects, the simplest way to do this would be to add a unique property to one of the objects at the time you need to compare the objects, check if the property exists in the other and then remove it again. This saves overriding prototypes.
function isSameObject(objectA, objectB) {
unique_ref = "unique_id_" + performance.now();
objectA[unique_ref] = true;
isSame = objectB.hasOwnProperty(unique_ref);
delete objectA[unique_ref];
return isSame;
}
object1 = {something:true};
object2 = {something:true};
object3 = object1;
console.log(isSameObject(object1, object2)); //false
console.log(isSameObject(object1, object3)); //true
I faced the same problem and here's the solution I implemented with ES6
code
let id = 0; // This is a kind of global variable accessible for every instance
class Animal {
constructor(name){
this.name = name;
this.id = id++;
}
foo(){}
// Executes some cool stuff
}
cat = new Animal("Catty");
console.log(cat.id) // 1
I've used code like this, which will cause Objects to stringify with unique strings:
Object.prototype.__defineGetter__('__id__', function () {
var gid = 0;
return function(){
var id = gid++;
this.__proto__ = {
__proto__: this.__proto__,
get __id__(){ return id }
};
return id;
}
}.call() );
Object.prototype.toString = function () {
return '[Object ' + this.__id__ + ']';
};
the __proto__ bits are to keep the __id__ getter from showing up in the object. this has been only tested in firefox.
Notwithstanding the advice not to modify Object.prototype, this can still be really useful for testing, within a limited scope. The author of the accepted answer changed it, but is still setting Object.id, which doesn't make sense to me. Here's a snippet that does the job:
// Generates a unique, read-only id for an object.
// The _uid is generated for the object the first time it's accessed.
(function() {
var id = 0;
Object.defineProperty(Object.prototype, '_uid', {
// The prototype getter sets up a property on the instance. Because
// the new instance-prop masks this one, we know this will only ever
// be called at most once for any given object.
get: function () {
Object.defineProperty(this, '_uid', {
value: id++,
writable: false,
enumerable: false,
});
return this._uid;
},
enumerable: false,
});
})();
function assert(p) { if (!p) throw Error('Not!'); }
var obj = {};
assert(obj._uid == 0);
assert({}._uid == 1);
assert([]._uid == 2);
assert(obj._uid == 0); // still
This one will calculate a HashCode for each object, optimized for string, number and virtually anything that has a getHashCode function. For the rest it assigns a new reference number.
(function() {
var __gRefID = 0;
window.getHashCode = function(ref)
{
if (ref == null) { throw Error("Unable to calculate HashCode on a null reference"); }
// already cached reference id
if (ref.hasOwnProperty("__refID")) { return ref["__refID"]; }
// numbers are already hashcodes
if (typeof ref === "number") { return ref; }
// strings are immutable, so we need to calculate this every time
if (typeof ref === "string")
{
var hash = 0, i, chr;
for (i = 0; i < ref.length; i++) {
chr = ref.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash;
}
// virtual call
if (typeof ref.getHashCode === "function") { return ref.getHashCode(); }
// generate and return a new reference id
return (ref["__refID"] = "ref" + __gRefID++);
}
})();
If you came here because you deal with class instances like me you can use static vars/methods to reference instances by a custom unique id:
class Person {
constructor( name ) {
this.name = name;
this.id = Person.ix++;
Person.stack[ this.id ] = this;
}
}
Person.ix = 0;
Person.stack = {};
Person.byId = id => Person.stack[ id ];
let store = {};
store[ new Person( "joe" ).id ] = true;
store[ new Person( "tim" ).id ] = true;
for( let id in store ) {
console.log( Person.byId( id ).name );
}
Here's a variant of Justin Johnson's answer that provides a scalability benefit when you are creating billions of objects for which you want the ID.
Specifically, rather than solely using a 1-up counter (that might overflow the representational limits of Number, and can't be cycled without risking reusing an ID), we register the object and its newly generated ID with a FinalizationRegistry, such that, at some point after the object is garbage collected, the ID is returned to a freelist for reuse by a newly created object (Python's id function can also return the same ID for multiple objects, so long as the existence of the two objects does not overlap in time).
Limitations:
It only works on objects, not JS primitives (this is somewhat reasonable; unlike Python, where everything is an object, JS primitives typically aren't, and the id function logically only works on objects, since primitives need not "exist" in any reasonably identifiable way).
If the code creates (without discarding) billions of objects, asks for their IDs, then releases them all at once and never asks for an ID again, the recovered IDs in the freelist constitute a memory leak of sorts. Hopefully the JS optimizer stores them efficiently, so the cost remains a small fraction of what the objects themselves cost, but it's still a cost. In cases where objects with IDs are regularly created and destroyed, the wasted memory is roughly tied to the maximum number of such ID-ed objects in existence at any given point in time.
If those limitations aren't a problem though, this works fairly well. I modified the testing code a bit to hand control back to the event loop (and hopefully the garbage collector) now and again while creating 10M garbage objects to ID, and on my browser, nearly half the object IDs get reclaimed for reuse; the final loop making five objects and IDing them produces IDs just above 1M, when over 2M objects had IDs generated at some point. In a realistic scenario with meaningful code executing and real async usage I'd expect better results simply because there would be more opportunities for the finalization registry to perform cleanup.
async function sleep(ms) {
await _sleep(ms);
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(function() {
if ( typeof Object.id != "undefined" ) return;
var freelist = []; // Stores previously used IDs for reuse when an object with
// an ID is garbage collected, so creating and dropping billions
// of objects doesn't consume all available IDs
const registry = new FinalizationRegistry((freeid) => {
freelist.push(freeid);
});
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: freelist.length ? freelist.pop() : ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
registry.register(o, o.__uniqueid); // Sometime after o is collected, its ID
// will be reclaimed for use by a new object
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
var idsum = 0; // So we do something real to prevent optimizing out code
// Make a ton of temporary objects with IDs, handing control back to the event loop
// every once in a while to (hopefully) see some IDs returned to the pool
for (var i = 0; i < 1000000; ++i) {
idsum += Object.id({c: i});
}
sleep(10).then(() => {
console.log(Object.id(function() { console.log("Hey"); }));
for (var i = 1000000; i < 2000000; ++i) {
idsum += Object.id({c: i});
}
console.log(Object.id(function() { console.log("There"); }));
sleep(10).then(() => {
for (var i = 0; i < 5; ++i) {
console.log(Object.id([i]));
}
console.log(idsum);
});
});
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`

Categories