javascript中的唯一对象标识符

时间:2010-01-04 05:34:50

标签: javascript

我需要做一些实验,我需要知道javascript中对象的某种唯一标识符,所以我可以看看它们是否相同。我不想使用相等运算符,我需要类似python中的id()函数。

这样的事情是否存在?

13 个答案:

答案 0 :(得分:59)

更新我的原始答案是在6年前写成的,符合时代和我的理解。回应评论中的一些对话,更现代的方法如下:

(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`

如果您有Object.defineProperty的浏览器兼容性,则需要check here的古老浏览器要求。

原始答案保留在下方(而不仅仅是在更改历史记录中),因为我认为这种比较很有价值。


您可以进行以下操作。这也为您提供了在其构造函数或其他位置显式设置对象ID的选项。

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

注意确保您用于内部存储唯一ID的任何成员不会与另一个自动创建的成员名称冲突。

答案 1 :(得分:36)

就我的观察而言,此处发布的任何答案都会产生意想不到的副作用。

在与ES2015兼容的环境中,您可以使用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

答案 2 :(得分:35)

最新的浏览器为扩展Object.prototype提供了一种更干净的方法。此代码将使属性对属性枚举隐藏(对于p in o)

对于browsers that implement defineProperty,您可以实现uniqueId属性,如下所示:

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

有关详细信息,请参阅https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty

答案 3 :(得分:10)

实际上,您不需要修改object原型并在那里添加功能。以下内容适用于您的目的。

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

答案 4 :(得分:6)

对于实现Object.defineProperty()方法的浏览器,下面的代码生成并返回一个可以绑定到您拥有的任何对象的函数。

这种方法的优点是不会扩展Object.prototype

代码通过检查给定对象是否具有__objectID__属性,并将其定义为隐藏(不可枚举)只读属性来工作。

因此,在定义了只读obj.__objectID__属性后,可以安全地更改或重新定义只读__objectID__属性,并始终抛出一个错误的错误,而不是默默地失败。

最后,在极端情况下,某些其他代码已经在给定对象上定义了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__; } }; })(); ,这个值就会被返回。

{{1}}

答案 5 :(得分:3)

jQuery代码使用它自己的data()方法作为id。

var id = $.data(object);

在后台方法data在名为object的{​​{1}}中创建一个非常特殊的字段,其中包含唯一ID的下一个ID,例如

"jQuery" + now()

我建议您使用与John Resig相同的方法,显然知道所有关于JavaScript的方法,并且他的方法基于所有这些知识。

答案 6 :(得分:3)

@justin的打字稿版本答案,ES6兼容,使用符号来防止任何键冲突并添加到全局Object.id中以方便使用。只需复制粘贴下面的代码,或将其放入您将导入的ObjecId.ts文件中。

(enableObjectID)();

const uniqueId: symbol = Symbol('The unique id of an object');
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];
    };
}

使用ts代码:

(<any>Object).id(yourObject);

答案 7 :(得分:1)

我使用过这样的代码,这将导致Objects使用唯一的字符串进行字符串化:

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__ + ']';
};

__proto__位是为了防止__id__ getter出现在对象中。这只是在firefox中测试过的。

答案 8 :(得分:1)

尽管不修改Object.prototype的建议,但在有限的范围内,这对于测试仍然非常有用。接受的答案的作者改变了它,但仍然设置Object.id,这对我没有意义。这是一个完成这项工作的片段:

// 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

答案 9 :(得分:1)

我遇到了同样的问题,这是我用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 

答案 10 :(得分:1)

出于比较两个对象的目的,最简单的方法是在需要比较对象时向其中一个对象添加唯一属性,检查该属性是否存在于另一个对象中,然后删除再来一次。这样可以节省压倒一切的原型。

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

答案 11 :(得分:0)

这将为每个对象计算一个HashCode,并针对stringnumber和几乎所有具有getHashCode功能的对象进行优化。其余的它分配一个新的参考号。

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

答案 12 :(得分:0)

如果你来这里是因为你像我一样处理类实例,你可以使用静态变量/方法通过自定义唯一 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 );
}