javascript'te benzersiz nesne tanımlayıcı


121

Biraz deney yapmam ve javascript'teki nesneler için bir tür benzersiz tanımlayıcı bilmem gerekiyor, böylece aynı olup olmadıklarını görebilirim. Eşitlik operatörlerini kullanmak istemiyorum, python'daki id () işlevi gibi bir şeye ihtiyacım var.

Böyle bir şey var mı?


2
Biraz merak ediyorum, neden eşitlik operatörlerinden kaçınmak istiyorsunuz?
CMS

22
çünkü basit bir şey istiyorum ve bir sayı görmek istiyorum, net bir şey. Bu dil yavru kediyi ağlatıyor, zaten yeterince savaşıyorum.
Stefano Borini

4
Katı eşitlik operatörü (===) nesneler üzerinde istediğinizi yapacaktır (sayıları / dizgileri / vb. Karşılaştırıyorsanız, aynı şey değildir) ve her nesneye gizli bir benzersiz kimlik oluşturmaktan daha basittir.
Ben Zotto

4
@CMS @Ben Benzersiz kimliklere sahip olmak, bir IdentitySet gibi şeyleri hata ayıklamak veya uygulamak için yararlı olabilir.
Alex Jasmin

9
Javascript anlayışında bir atılım yaptığımı belirtmek isterim. bir tür sinapsı kapattı. Şimdi anlaşıldı. Bir şeyler gördüm. JavaScript programcılığında bir seviye kazandım.
Stefano Borini

Yanıtlar:


69

Güncelle Aşağıdaki orijinal cevabım 6 yıl önce zamana ve anlayışıma uygun bir tarzda yazılmıştır. Yorumlarda yer alan bazı konuşmalara yanıt olarak, buna daha modern bir yaklaşım şu şekildedir:

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

Arkaik tarayıcı gereksinimleriniz varsa, için tarayıcı uyumluluğu için burayı kontrol edinObject.defineProperty .

Orijinal cevap aşağıda tutulmuştur (sadece değişiklik geçmişinde değil) çünkü karşılaştırmanın değerli olduğunu düşünüyorum.


Aşağıdakileri bir dönüş yapabilirsiniz. Bu ayrıca size bir nesnenin kimliğini yapıcısında veya başka bir yerde açıkça ayarlama seçeneği sunar.

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

Benzersiz kimliği dahili olarak depolamak için kullandığınız üye ne olursa olsun, otomatik olarak oluşturulan başka bir üye adıyla çakışmadığından emin olun.


1
@Justin Object.prototype'a özellik eklemek ECMAScript 3'te sorunludur çünkü bu özellikler tüm nesnelerde numaralandırılabilir. Dolayısıyla, Object.prototype.a'yı tanımlarsanız , (prop in {}) alert (prop) için yaptığınızda "a" görünecektir ; Dolayısıyla, Object.prototype'ı artırmak ile for..in döngüsü kullanan nesneler gibi kayıt boyunca yineleme yapmak arasında bir uzlaşma sağlamalısınız. Bu kütüphaneler için ciddi bir sorundur
Alex Jasmin

29
Taviz yok. object.hasOwnProperty(member)Bir for..indöngü kullanırken her zaman kullanmak için uzun zamandır en iyi uygulama olarak kabul edildi . Bu iyi belgelenmiş bir uygulamadır ve jslint tarafından uygulanmaktadır
Justin Johnson

3
Ben de bunu yapmayı tavsiye etmem, en azından her nesne için değil, aynısını sadece işlediğiniz nesnelerle de yapabilirsiniz. ne yazık ki çoğu zaman harici javascript kitaplıkları kullanmak zorunda kalıyoruz ve maalesef bunların her biri iyi programlanmadığından, web sayfanızda bulunan tüm kitaplıkların tam kontrolüne sahip değilseniz veya en azından iyi işlediklerini bilmiyorsanız, bundan kaçının .
işe yaramaz

1
@JustinJohnson: ES5 içinde bir karar vermek gereklidir: defineProperty(…, {enumerable:false}). Ve uidyöntemin kendisi yine de Objectad alanında olmalıdır
Bergi

@JustinJohnson, eğer nesne kimliğinin olduğunu söyleyelim, 4o zaman 4 kimliğine sahip bir nesneyi bir değişkene nasıl atardınız, böylece onunla bir şeyler yapabilirsiniz ... özelliklerine erişmek gibi?
Efendim

51

Benim gözlemime göre, burada yayınlanan herhangi bir yanıtın beklenmedik yan etkileri olabilir.

ES2015 uyumlu ortamda, WeakMap kullanarak herhangi bir yan etkiden kaçınabilirsiniz .

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

1
Neden olmasın return currentId?
Gust van de Wal

Neden WeakMapaksine Map? Bir dizge veya sayı sağlarsanız, bu işlev çökecektir.
Nate Symer

35

En son tarayıcılar, Object.prototype'ı genişletmek için daha temiz bir yöntem sağlar. Bu kod, özelliği özellik numaralandırmasından gizleyecektir (p in o için)

DefineProperty uygulayan tarayıcılar için, uniqueId özelliğini şu şekilde uygulayabilirsiniz:

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

Ayrıntılar için bkz. Https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty


2
Görünüşe göre "en son tarayıcılar" Firefox 3.6'yı içermiyor. (Evet, Firefox'un daha yeni sürümlerinde yükseltme yarışından çekiliyorum ve eminim tek ben değilim. Ayrıca, FF3.6 sadece 1 yaşında.)
bart

7
1 yaşında bu sporda "sadece" değil. Web dinamik olarak gelişiyor - bu iyi bir şey. Modern tarayıcılar, tam olarak buna izin vermek amacıyla otomatik güncelleyicilere sahiptir.
Kos

1
Birkaç yıl sonra, bu benim için kabul edilen cevaptan daha iyi çalışıyor gibi görünüyor.
Fitter Man

11

Aslında, objectprototipi değiştirmenize ve oraya bir işlev eklemenize gerek yoktur. Aşağıdakiler amacınız için iyi çalışmalıdır.

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
nocase, snake_case ve camelCase, üçünü de 6 satırlık bir kod parçacığına dönüştürdü. Bunu her gün görmüyorsunuz
Gust van de Wal

bu, hacklerden çok daha ikna edici bir mekanizma gibi görünüyor object. sadece OP'nin nesne eşleşmesini istediğinizde çalıştırmanız gerekir ve geri kalan zamanlarda yolun dışında kalır.
JL Peyret

6

Object.defineProperty()Yöntemi uygulayan tarayıcılar için aşağıdaki kod, sahip olduğunuz herhangi bir nesneye bağlayabileceğiniz bir işlev oluşturur ve döndürür.

Bu yaklaşımın genişlememe avantajı vardır Object.prototype.

Kod, verilen nesnenin bir __objectID__özelliğe sahip olup olmadığını kontrol ederek ve yoksa onu gizli (numaralandırılamayan) salt okunur bir özellik olarak tanımlayarak çalışır.

Bu nedenle, tanımlandıktan sonra salt okunur obj.__objectID__özelliği değiştirme veya yeniden tanımlama girişimlerine karşı güvenlidir ve sessizce başarısız olmak yerine sürekli olarak güzel bir hata atar.

Son olarak, __objectID__belirli bir nesnede başka bir kodun zaten tanımlanmış olduğu oldukça uç durumda , bu değer basitçe döndürülür.

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

         }
    };

})();

4

jQuery kodu, data()id gibi kendi yöntemini kullanır .

var id = $.data(object);

Sahne arkası yöntemi data, object" "jQuery" + now()put there next id" adlı çok özel bir alan yaratır.

id = elem[ expando ] = ++uuid;

John Resig'in JavaScript hakkında her şeyi bildiği ve yöntemi tüm bu bilgilere dayandığı için aynı yöntemi kullanmanızı öneririm.


5
jQuery datayönteminin kusurları vardır. Örneğin stackoverflow.com/questions/1915341/… konusuna bakın . Ayrıca, John Resig hiçbir şekilde JavaScript hakkında bilinmesi gereken her şeyi bilmiyor ve bunun bir JavaScript geliştiricisi olarak size yardımcı olmayacağına inanmak.
Tim Down

1
@Tim, kaputun altında kabaca aynı şeyi yaptığından, burada sunulan diğer yöntemler kadar benzersiz kimlik elde etme açısından kusurları var. Ve evet, John Resig'in benden çok şey bildiğine inanıyorum ve Douglas Crockford olmasa bile kararlarından ders almalıyım.
vava

AFAIK $ .data , JavaScript nesnelerinde değil, yalnızca DOM öğelerinde çalışır.
mb21

4

@Justin answer'ın Typescript sürümü, ES6 uyumlu, herhangi bir anahtar çarpışmayı önlemek için Semboller kullanan ve kolaylık sağlamak için genel Object.id'ye eklenmiştir. Aşağıdaki kodu kopyalayıp yapıştırın veya içe aktaracağınız bir ObjecId.ts dosyasına koyun.

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

Kullanım örneği:

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

1

Nesnelerin benzersiz dizelerle dizilmesine neden olacak böyle bir kod kullandım:

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__bit tutmak için olan __id__bir nesne gösterilmesini getter. bu yalnızca firefox'ta test edilmiştir.


Bunun __defineGetter__standart olmadığını unutmayın .
Tomáš Zato - Monica'yı eski durumuna getir

1

Object.prototype'ı değiştirmeme tavsiyesine rağmen, bu sınırlı bir kapsam dahilinde test etmek için gerçekten yararlı olabilir. Kabul edilen cevabın yazarı bunu değiştirdi, ancak yine de ayarlıyor Object.id, bu bana mantıklı gelmiyor. İşte işi yapan bir pasaj:

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

1

Aynı problemle karşılaştım ve işte ES6 ile uyguladığım çözüm

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 

1

İki nesneyi karşılaştırmak amacıyla, bunu yapmanın en basit yolu, nesneleri karşılaştırmanız gerektiğinde nesnelerden birine benzersiz bir özellik eklemek, diğerinde özelliğin var olup olmadığını kontrol etmek ve ardından tekrar kaldırmaktır. Bu, geçersiz kılınan prototipleri kurtarır.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.