152

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 ?

Stefano Borini
  • 132,232
  • 95
  • 283
  • 413
  • 3
    I'm a bit curious, why you want to avoid the equality operators? – Christian C. Salvadó Jan 04 '10 at 05:42
  • 32
    because I want something simple, and I want to see a number, something clear. This language makes kitten cry, I am already fighting it enough. – Stefano Borini Jan 04 '10 at 05:45
  • 5
    The strict equality operator (===) will do what you ask for on objects (if you're comparing numbers/strings/etc it's not the same thing), and is simpler than building a secret unique ID into every object. – Ben Zotto Jan 04 '10 at 06:31
  • 7
    @CMS @Ben Having unique ids can be useful for debugging or implementing things like an IdentitySet. – Alex Jasmin Jan 04 '10 at 06:45
  • 1
    @Ben, actually both `==` and `===` behave the same for object references... see the step 13 of both algorithms: http://bclary.com/2004/11/07/#a-11.9.3 http://bclary.com/2004/11/07/#a-11.9.6 – Christian C. Salvadó Jan 04 '10 at 06:46
  • 11
    I wish to communicate that I did a breakthrough in the understanding of javascript. kind of closed a synapse. Now everything is clear. I've seen things. I gained a level in javascript programmer. – Stefano Borini Jan 04 '10 at 06:48
  • ummm, how can JS do a strict comparison between objects without a unique id, that's impossible, I don't get it – Alexander Mills Feb 16 '15 at 08:08
  • 1
    I came here from google because I need to use object identifiers as map indexes - in C++, you'd use pointers, making for example map of ``. So in JS, I wanted to make array that will form unique key for every object, so that `myArray[{}]` does allways access new location. – Tomáš Zato - Reinstate Monica Sep 29 '15 at 21:38
  • @AlexanderMills, they compare reference address, probably – Rodrigo Rodrigues Jul 13 '19 at 22:51

13 Answers13

80

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" ) {
            var id = 0;

            Object.id = function(o) {
                if ( typeof o.__uniqueid == "undefined" ) {
                    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.

Ran Marciano
  • 1,298
  • 5
  • 11
  • 28
Justin Johnson
  • 30,312
  • 7
  • 62
  • 87
  • 1
    @Justin Adding properties to Object.prototype is problematic in ECMAScript 3 because these proprieties are enumerable on all objects. So if you define *Object.prototype.a* then "a" will be visible when you do *for (prop in {}) alert(prop);* So you have to make a compromise between augmenting *Object.prototype* and being able to iterate through record like objects using a for..in loop. This is a serious problem for libraries – Alex Jasmin Jan 04 '10 at 22:06
  • 29
    There is no compromise. It's long been considered a best practice to always use `object.hasOwnProperty(member)` when using a `for..in` loop. This is a well documented practice and one that is enforced by jslint – Justin Johnson Jan 04 '10 at 23:01
  • 3
    neither of i would recomend to do this also, at least not for every object, you can do the same just with the objects you handle. unfortunately most of the time we have to use external javascript libraries, and unfortunately not every one of them are well programed so avoid this unless you have total control of all of the libraries included in your web page, or at least you know they handled well. – useless Mar 14 '11 at 23:32
  • 1
    @JustinJohnson: There is a compromise in ES5: `defineProperty(…, {enumerable:false})`. And the `uid` method itself [should be in the `Object` namespace anyway](http://stackoverflow.com/q/13239317/1048572) – Bergi Dec 18 '12 at 22:16
  • @JustinJohnson if lets say the object id was `4` how would you then assign obj with the id 4 to a variable so you can do stuff with it...like acess its properties? – Sir Jan 17 '15 at 09:20
  • @JustinJohnson I agree that enumeration should be guarded, but how would you prevent `__uniqueid` from enumerating? `var x = {a:0,b:1}; for(var i in x) if(x.hasOwnProperty(i)) console.log(i); /* so far so good: a, b */ x.uniqueId(); for (var i in x) if (x.hasOwnProperty(i)) console.log(i); /* whops: a, b, __uniqueid */` Bergi's code helps, but not pre-ES5. – TWiStErRob Feb 09 '16 at 13:24
  • FWIW, this also works when you replace `==` with `===` (as you might do if you need to convert this to Coffeescript). – Pistos Aug 19 '16 at 15:09
  • Since `Object.id` is a function that needs to be called manually, there's no reason to put it on the global `Object` that we don't own, which, in my opinion, is no better than on the `Object.prototype`. – Emile Bergeron May 21 '19 at 21:56
69

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
hakatashi
  • 7,447
  • 2
  • 19
  • 21
36

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

bignose
  • 27,414
  • 13
  • 72
  • 104
Aleksandar Totic
  • 2,519
  • 25
  • 26
  • 2
    "Latest browsers" apparently doesn't include Firefox 3.6. (Yes I'm opting out of the upgrade race in the newer versions of Firefox, and I'm sure I'm not the only one. Besides, FF3.6 is only 1 year old.) – bart Sep 28 '11 at 08:06
  • 7
    1 year old isn't "only" in this sport. The web evolves dynamically - that's a good thing. Modern browsers have automatic updaters precisely for the purpose of allowing that. – Kos Nov 24 '12 at 09:02
  • 1
    A few years later, this seems to be working better for me than the accepted answer. – Fitter Man Feb 14 '15 at 23:02
13

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;
}
KalEl
  • 8,557
  • 13
  • 44
  • 56
  • 17
    nocase, snake_case *and* camelCase have all three made their way into a 6-line code snippet. You don't see that everyday – Gust van de Wal Nov 06 '19 at 13:45
  • 3
    this seems like a much more convincing mechanism than `object` hacks. you need only run it when you want the OP's object matching and it stays out of the way the rest of the time. – JL Peyret Feb 13 '20 at 17:38
  • But if you copy the object (e.g. { …obj } ) you copy the obj.__obj_id with it. The point would be to prevent that. – Gábor Angyal Dec 20 '21 at 05:21
7

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

         }
    };

})();
Luc125
  • 5,615
  • 30
  • 35
5

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));
Flavien Volken
  • 16,172
  • 11
  • 86
  • 115
4

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.

vava
  • 23,905
  • 11
  • 61
  • 78
  • 6
    jQuery's `data` method has flaws. See for example http://stackoverflow.com/questions/1915341/whats-wrong-with-adding-properties-to-dom-element-objects/1915699#1915699. Also, John Resig by no means knows all there is to know about JavaScript, and believing that he does will not help you as a JavaScript developer. – Tim Down Jan 04 '10 at 09:51
  • 1
    @Tim, it has as much flaws in terms of getting unique id as any other method presented here since it does roughly the same under the hood. And yes, I believe John Resig knows a lot more than me and I should be learning from his decisions even if he's not Douglas Crockford. – vava Jan 04 '10 at 10:01
  • AFAIK [$.data](http://api.jquery.com/jquery.data/) doesn't work on JavaScript objects but only DOM elements. – mb21 Jun 12 '14 at 11:07
4

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
adevart
  • 146
  • 4
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.

Eric Strom
  • 39,423
  • 2
  • 77
  • 152
1

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
Klortho
  • 535
  • 5
  • 9
1

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 
Jaime Rios
  • 1,678
  • 2
  • 13
  • 20
0

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++);
  }
})();
Ivan Sanz Carasa
  • 887
  • 5
  • 14
0

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 );
}
Juergen Riemer
  • 1,313
  • 1
  • 12
  • 29