unique object identifier in javascript

Viewed 155145

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 ?

13 Answers

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.

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;
}

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'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.

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 );
}

Related