Javascript 中唯一的对象标识符

我需要做一些实验,我需要知道一些 javascript 对象的唯一标识符,这样我就可以看到它们是否相同。我不想使用等式运算符,我需要在 python 中使用 id ()函数。

这种东西真的存在吗?

159725 次浏览

我最初的回答是在6年前写的,风格符合时代和我的理解。针对评论中的一些对话,一种更现代的做法如下:

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

如果你有过时的浏览器要求,看这里浏览器兼容性为 Object.defineProperty

最初的答案保留在下面(而不仅仅是在更改历史中) ,因为我认为比较是有价值的。


您可以对以下内容进行修改。这还使您可以选择在对象的构造函数或其他地方显式设置对象的 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 的任何成员不会与另一个自动创建的成员名冲突。

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

var id = $.data(object);

在后台方法 dataobject中创建了一个非常特殊的字段,称为 "jQuery" + now()

id = elem[ expando ] = ++uuid;

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

我曾经使用过这样的代码,它会导致 Object 用唯一的字符串串化:

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__获取器出现在对象中。这只在 Firefox 中进行过测试。

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

对于 实现 defeProperty 的浏览器,您可以像下面这样实现 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

实际上,您不需要修改 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;
}

对于实现 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__;


}
};


})();

尽管建议不要修改 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

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

根据我的观察,这里发布的任何答案都可能产生意想不到的副作用。

在与 ES2015兼容的环境中,可以使用 弱点地图避免任何副作用。

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

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

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

用法示例:

console.log(Object.id(myObject));

为了比较两个对象,最简单的方法是在需要比较对象时向其中一个对象添加唯一的属性,检查另一个对象中是否存在该属性,然后再次删除它。这样可以保存重写原型。

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

这个程序将为每个对象计算一个 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++);
}
})();

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