JavaScript Object Id
Asked Answered
C

5

106

Do JavaScript objects/variables have some sort of unique identifier? Like Ruby has object_id. I don't mean the DOM id attribute, but rather some sort of memory address of some kind.

Crossroad answered 7/1, 2010 at 13:43 Comment(4)
Are you looking to compare objects using object_id?Handed
See #1998161Hearsay
this is definitely not true that the question is answered elsewhere. not even close.Brachylogy
@ErikAronesty I agree. Nowhere in the linked thread is there an answer to the question of whether objects have a built-in id.Sciurine
I
55

No, objects don't have a built in identifier, though you can add one by modifying the object prototype. Here's an example of how you might do that:

(function() {
    var id = 0;

    function generateId() { return id++; };

    Object.prototype.id = function() {
        var newId = generateId();

        this.id = function() { return newId; };

        return newId;
    };
})();

That said, in general modifying the object prototype is considered very bad practice. I would instead recommend that you manually assign an id to objects as needed or use a touch function as others have suggested.

Inmost answered 7/1, 2010 at 14:14 Comment(4)
ActionScript 3 has a Dictionary object that uses strict equality for key comparison, so one can use objects as keys. Is there an equivalent in JavaScript, or would you have to manually construct unique id's for every object (either via Object.prototype or manually adding an id to select objects)?Finely
Unfortunately javascript doesn't have anything like that. Sounds like you will have to give objects id and then use those id's as keys in object, as you suggested. That said, if you really want to be clever, you could implement "id objects" by overriding the toString method and using them like this id = new Id(); cache[id] = obj. It's a little nuts but pretty interesting. Here's an article I wrote that explain this technique in more detail: xavi.co/articles/fun-with-tostring-in-javascriptInmost
Okay, I just found out why. jQuery also overrides ID and somehow my page broke when I overrode it. Hah. Okay. So. I'll just avoid naming issues and cross my fingers.Columbous
This example could be used as a great usage example for closures and prototypal inheritance.Palmieri
G
75

If you want to lookup/associate an object with a unique identifier without modifying the underlying object, you can use a WeakMap:

// Note that object must be an object or array,
// NOT a primitive value like string, number, etc.
var objIdMap=new WeakMap, objectCount = 0;
function objectId(object){
  if (!objIdMap.has(object)) objIdMap.set(object,++objectCount);
  return objIdMap.get(object);
}

var o1={}, o2={}, o3={a:1}, o4={a:1};
console.log( objectId(o1) ) // 1
console.log( objectId(o2) ) // 2
console.log( objectId(o1) ) // 1
console.log( objectId(o3) ) // 3
console.log( objectId(o4) ) // 4
console.log( objectId(o3) ) // 3

Using a WeakMap instead of Map ensures that the objects can still be garbage-collected.

Greatest answered 10/2, 2016 at 2:56 Comment(2)
Best answer! Uses memory O(n) where n is number of objects whose IDs you care about (rather than n=all objects) and doesn't affect garbage collection.Agalloch
Bravo! Even works in IE11 (tested on Win10)Jeanniejeannine
I
55

No, objects don't have a built in identifier, though you can add one by modifying the object prototype. Here's an example of how you might do that:

(function() {
    var id = 0;

    function generateId() { return id++; };

    Object.prototype.id = function() {
        var newId = generateId();

        this.id = function() { return newId; };

        return newId;
    };
})();

That said, in general modifying the object prototype is considered very bad practice. I would instead recommend that you manually assign an id to objects as needed or use a touch function as others have suggested.

Inmost answered 7/1, 2010 at 14:14 Comment(4)
ActionScript 3 has a Dictionary object that uses strict equality for key comparison, so one can use objects as keys. Is there an equivalent in JavaScript, or would you have to manually construct unique id's for every object (either via Object.prototype or manually adding an id to select objects)?Finely
Unfortunately javascript doesn't have anything like that. Sounds like you will have to give objects id and then use those id's as keys in object, as you suggested. That said, if you really want to be clever, you could implement "id objects" by overriding the toString method and using them like this id = new Id(); cache[id] = obj. It's a little nuts but pretty interesting. Here's an article I wrote that explain this technique in more detail: xavi.co/articles/fun-with-tostring-in-javascriptInmost
Okay, I just found out why. jQuery also overrides ID and somehow my page broke when I overrode it. Hah. Okay. So. I'll just avoid naming issues and cross my fingers.Columbous
This example could be used as a great usage example for closures and prototypal inheritance.Palmieri
M
11

Actually, you don't need to modify the object prototype. The following should work to 'obtain' unique ids for any object, efficiently enough.

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;
}
Modality answered 31/3, 2012 at 15:46 Comment(2)
Just be aware that this will NOT play nicely with most ways of copying objects, if you expect the objects to have different ids afterwards.Agrarian
Indeed, you'd need a special "copyObject" function that takes this __obj_id into account specifically. You'd also have to be sure that there are no conflicts with the use of "__obj_id" in other libraries. This is so much easier in ActionScript, whose Dictionary object uses strict equality comparison on keys, including objects used as keys. In fact, you could probably write a Dictionary class in JS that automatically attaches ids this way to objects that are added to it as keys. It's like quantum mechanical JavaScript IDs; they don't exist until you try to observe them with this function, haha.Finely
I
2

I've just come across this, and thought I'd add my thoughts. As others have suggested, I'd recommend manually adding IDs, but if you really want something close to what you've described, you could use this:

var objectId = (function () {
    var allObjects = [];

    var f = function(obj) {
        if (allObjects.indexOf(obj) === -1) {
            allObjects.push(obj);
        }
        return allObjects.indexOf(obj);
    }
    f.clear = function() {
      allObjects = [];
    };
    return f;
})();

You can get any object's ID by calling objectId(obj). Then if you want the id to be a property of the object, you can either extend the prototype:

Object.prototype.id = function () {
    return objectId(this);
}

or you can manually add an ID to each object by adding a similar function as a method.

The major caveat is that this will prevent the garbage collector from destroying objects when they drop out of scope... they will never drop out of the scope of the allObjects array, so you might find memory leaks are an issue. If your set on using this method, you should do so for debugging purpose only. When needed, you can do objectId.clear() to clear the allObjects and let the GC do its job (but from that point the object ids will all be reset).

Impressive answered 21/4, 2014 at 19:12 Comment(2)
I think this is a slow-but-robust solution, and can be improved a little: ``` var objectId = (function() { var mem = []; var f = function(obj) { var r = mem.indexOf(obj); if (r === -1) { r = mem.length; mem.push(obj); } return r; }; f.reset = function() { return mem = []; }; return f; })(); ```Desiredesirea
@luochen1990, I think you'd be surprised at the speed of it. (But you're right, it's better practice to put the indexOf() result in a variable, although I would argue it from a DRY perspective rather than optimisation.) As long as the GC issue can be effectively managed, I think you'd have to have a helluva lot of objects to notice any significant performance impact.Impressive
R
0

const log = console.log;

function* generateId() {
  for(let i = 0; ; ++i) {
    yield i;
  }
}

const idGenerator = generateId();

const ObjectWithId = new Proxy(Object, {
  construct(target, args) {
    const instance = Reflect.construct(target, args);
    instance['id'] = idGenerator.next().value;
    return instance;
  }
})

const myObject = new ObjectWithId({
  name: '@@NativeObject'
});

log(myObject.id);
Real answered 22/12, 2021 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.