tags:

views:

591

answers:

5

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 ?

A: 
<script type="text/javascript">
if (Object.prototype != undefined || Object.prototype != null) {
    // This is generally not a good practice as onwards
    // this function will going to be attached
    // to all native/user defined objects. i.e.
    // object create with {}, [], new etc.
    Object.prototype.uniqueID = function() {
            return Math.floor(Math.random() * 1000);
    }
}
var obj1 = {};
var obj2 = new Object();

alert(obj1.uniqueID());
alert(obj2.uniqueID());
alert([].uniqueID());
alert({}.uniqueID());
</script>
Ramiz Uddin
jake sully once told me that random numbers are not necessarily unique. >_____<
Lukman
That will be different everytime
Justin Johnson
+4  A: 

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.

Justin Johnson
You really shouldn't add to `Object.prototype`...
J-P
@J-P: Any specific reasons?
Justin Johnson
@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
Alexandre Jasmin
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
A: 

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
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
@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
A: 

If you want to include all objects, including those defined using a special literal syntax (such as {}, [], /regex/, functions, ...), then no.

That is . . . unless you are willing to manually generate an id for every object you create and for every built-in/imported object:

var genId, getId, genIdRecursive;

(function () {
  var hiddenProps = [
      "prototype"
    , "toString"
    , "length"
    , ">>> REPLACE ME WITH ALL OTHER PROPS FOR-IN DOESN'T LOOP OVER! <<<"
    ];

  function isObj (x) {
    var type = typeof x
    return x !== null && (type === "object" || type === "function");
  }

  var idProp = "%*genId.idProp*%";
  var currId = 1 << 31;
  var maxId = -((1 << 31) + 1);

  genId = function () {
    for (var i = 0; i < arguments.length; ++i) {
      if (currId >= maxId) {
        return Math.random ();
      }
      var arg = arguments [i];
      if (isObj (arg)) {
        arg [idProp] = currId++;
      }
    }
  }

  getId = function (obj) {
    var id = obj [idProp];
    return id === undefined ? null : id;
  }

  genIdRecursive = function () {
    for (var i = 0; i < arguments.length; ++i) {
      var arg = arguments [i];
      if (!isObj (arg)) {
        continue;
      }
      try {
        if (getId (arg) !== null) {
          continue;
        }
        genId (arg);
      }
      catch (e) {
        continue;
      }
      var props = [];
      for (var prop in arg) {
         props.push (prop);
      }
      var nPs = props.length;
      var nHs = hiddenProps.length;
      var n = nHs + nPs;
      for (var i = 0; i < n; ++i) {
        var prop = i < nHs
          ? hiddenProps [i]
          : props [i - nHs]
          ;
        var val;
        try {
          val = arg [prop];
        }
        catch (e) {
          continue;
        }
        genIdRecursive (val);
      }
    }
  };

  genId (genId, getId, genIdRecursive);
}) ();

Examples:

function func1 () {}
genId (func1);

var func2 = genId (function () {});

var xs = genId (["array literal"]);

var regex = genId (/regex/);

var foo = genId ({ foo: "foo" });

var bar = genId (new Bar ());
trinithis
A: 

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