javascript中的唯一对象标识符

IT技术 javascript
2021-01-21 13:49:12

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

这样的东西存在吗?

6个回答

更新我下面的原始答案是 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.

原始答案保留在下面(而不仅仅是在更改历史记录中),因为我认为比较是有value的。


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

@JustinJohnson 如果让我们说对象 id 是4你如何将 id 为 4 的 obj 分配给一个变量,这样你就可以用它做一些事情......比如访问它的属性?
2021-03-15 13:49:12
@JustinJohnson:ES5 中有一个妥协:defineProperty(…, {enumerable:false}). 无论如何uid方法本身应该在Object命名空间中
2021-03-24 13:49:12
我都不建议这样做,至少不是对每个对象都这样做,你可以只对你处理的对象做同样的事情。不幸的是,大多数时候我们不得不使用外部 javascript 库,不幸的是,并非每个库都经过良好编程,因此避免这种情况,除非您可以完全控制网页中包含的所有库,或者至少您知道它们处理得很好.
2021-03-25 13:49:12
没有妥协。长期以来,一直认为object.hasOwnProperty(member)使用for..in循环始终使用的最佳实践这是一种有据可查的实践,并且由 jslint 强制执行
2021-04-04 13:49:12
@Justin 向 Object.prototype 添加属性在 ECMAScript 3 中是有问题的,因为这些属性在所有对象上都是可枚举的。因此,如果您定义Object.prototype.a,那么当您执行for (prop in {}) alert(prop);时,“a”将可见 因此,您必须在增强Object.prototype和能够使用 for..in 循环遍历类似对象的记录之间做出妥协这对图书馆来说是一个严重的问题
2021-04-05 13:49:12

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

在 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
为什么WeakMapMap? 如果您为它提供字符串或数字,该函数将崩溃。
2021-03-18 13:49:12
@NateSymer 通过使用 Map,键持有对传递给它的对象的强引用。这可以防止它们被垃圾收集,并且是一个主要的内存泄漏
2021-03-28 13:49:12
为什么不return currentId呢?
2021-04-08 13:49:12

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

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

几年后,这对我来说似乎比接受的答案更好。
2021-03-19 13:49:12
“最新浏览器”显然不包括 Firefox 3.6。(是的,我选择退出较新版本 Firefox 的升级竞赛,而且我确定我不是唯一一个。此外,FF3.6 才 1 岁。)
2021-03-30 13:49:12
1 岁不是这项运动中的“唯一”。网络动态发展 - 这是一件好事。现代浏览器具有自动更新程序,正是为了实现这一点。
2021-04-11 13:49:12

实际上,您不需要修改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;
}
这似乎是比objecthack更有说服力的机制你只需要在你想要 OP 的对象匹配时运行它,并且它在其余时间不受影响。
2021-04-04 13:49:12
nocase、snake_casecamelCase 将所有三个都放入了一个6 行代码片段中。你不会每天都看到
2021-04-10 13:49:12

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

         }
    };

})();