2010-01-04 22 views
74

Ho bisogno di fare qualche esperimento e ho bisogno di conoscere qualche tipo di identificatore univoco per gli oggetti in javascript, così posso vedere se sono uguali. Non voglio usare operatori di uguaglianza, ho bisogno di qualcosa come la funzione id() in python.identificatore oggetto univoco in javascript

Esiste qualcosa del genere?

+2

io sono un po 'curioso, perché si vuole evitare gli operatori di uguaglianza? – CMS

+11

perché voglio qualcosa di semplice, e voglio vedere un numero, qualcosa di chiaro. Questo linguaggio fa piangere il gattino, sto già combattendo abbastanza. –

+3

L'operatore di uguaglianza rigorosa (===) farà ciò che chiedi sugli oggetti (se stai confrontando numeri/stringhe/ecc non è la stessa cosa) ed è più semplice della creazione di un ID univoco segreto in ogni oggetto. –

risposta

48

Aggiornamento La mia risposta originale di seguito è stata scritta 6 anni fa in uno stile che si addice ai tempi e alla mia comprensione. In risposta a qualche conversazione nei commenti, un approccio più moderno di questo è la seguente:

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

Se si dispone di requisiti del browser arcaici, check here per la compatibilità del browser per Object.defineProperty.

La risposta originale viene mantenuta di seguito (anziché solo nella cronologia delle modifiche) perché ritengo che il confronto sia valido.


È possibile dare il seguente risultato. Questo ti dà anche la possibilità di impostare in modo esplicito l'ID di un oggetto nel suo costruttore o altrove.

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

Fare attenzione per assicurarsi che qualsiasi membro si utilizza per memorizzare internamente l'ID univoco non entrare in collisione con un altro nome utente creato automaticamente.

+24

Non dovresti davvero aggiungere a 'Object.prototype' ... – James

+0

@ J-P: qualche motivo specifico? –

+1

@Justin L'aggiunta di proprietà a Object.prototype è problematica in ECMAScript 3 poiché queste proprietà sono enumerabili su tutti gli oggetti. Quindi se definisci * Object.prototype.a * allora "a" sarà visibile quando fai * for (prop in {}) alert (prop); * Quindi devi fare un compromesso tra l'aumento * di Object.prototype * e essere in grado di scorrere attraverso record come oggetti usando un ciclo for..in. Questo è un problema serio per le librerie –

3

jQuery code usa il proprio metodo data() come tale id.

var id = $.data(object); 

Al metodo di backstage data crea un campo molto speciale nel object chiamato "jQuery" + now() messo lì prossima id di un flusso di ID univoci come

id = elem[ expando ] = ++uuid; 

Io suggerirei di usare lo stesso metodo di John Resig ovviamente sa tutto su JavaScript e il suo metodo si basa su tutta quella conoscenza.

+4

Il metodo 'data' di jQuery ha dei difetti. Vedi ad esempio http://stackoverflow.com/questions/1915341/whats-wrong-with-adding-properties-to-dom-element-objects/1915699#1915699. Inoltre, John Resig non conosce affatto tutto ciò che c'è da sapere su JavaScript, e credere che lui non ti aiuterà come sviluppatore JavaScript. –

+1

@Tim, ha tanti difetti in termini di ottenere un ID univoco come qualsiasi altro metodo presentato qui poiché fa approssimativamente lo stesso sotto il cofano. E sì, credo che John Resig ne sappia molto più di me e dovrei imparare dalle sue decisioni anche se non è Douglas Crockford. – vava

+0

AFAIK [$ .data] (http://api.jquery.com/jquery.data/) non funziona sugli oggetti JavaScript ma solo sugli elementi DOM. – mb21

1

ho usato il codice come questo, che farà sì che gli oggetti di stringa i con le stringhe univoche:

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

i __proto__ bit sono per mantenere il __id__ getter da presentarsi nell'oggetto. questo è stato testato solo in Firefox.

+0

Ricorda che '__defineGetter__' non è standard. –

32

Gli ultimi browser forniscono un metodo più pulito per estendere Object.prototype. Questo codice renderà la proprietà nascosto dalla proprietà di enumerazione (per p in o)

Per il browsers that implement defineProperty, è possibile implementare proprietà UniqueID come questo:

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

Per ulteriori informazioni, vedere https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty

+2

"Ultimi browser" a quanto pare non include Firefox 3.6. (Sì, sto optando per la gara di aggiornamento nelle versioni più recenti di Firefox, e sono sicuro di non essere l'unico. Inoltre, FF3.6 ha solo 1 anno.) – bart

+7

1 anno non è "solo" in questo sport. Il web si evolve in modo dinamico: questa è una buona cosa. I browser moderni dispongono di updaters automatici proprio allo scopo di consentire ciò. – Kos

+1

Pochi anni dopo, questo sembra funzionare meglio per me rispetto alla risposta accettata. –

10

In realtà, non è necessario modificare il prototipo object e aggiungere una funzione lì. Il seguente dovrebbe funzionare bene per il tuo scopo.

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

Per browser che implementano il metodo Object.defineProperty(), il codice qui sotto genera e restituisce una funzione che è possibile associare a qualsiasi oggetto che si possiede.

Questo approccio ha il vantaggio di non estendere Object.prototype.

Il codice funziona controllando se l'oggetto specificato ha una proprietà __objectID__ e definendolo come proprietà di sola lettura nascosta (non enumerabile), in caso contrario.

Quindi è sicuro contro qualsiasi tentativo di modificare o ridefinire la proprietà di sola lettura obj.__objectID__ dopo che è stata definita e genera costantemente un errore piacevole invece di fallire in modo silenzioso.

Infine, nel caso estremo in cui qualche altro codice avrebbe già definito __objectID__ su un dato oggetto, questo valore verrebbe semplicemente restituito.

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

Nonostante il consiglio di non modificare Object.prototype, questo può essere ancora molto utile per i test, in un ambito limitato. L'autore della risposta accettata lo ha cambiato, ma sta ancora impostando Object.id, che per me non ha senso. Ecco un frammento di codice che fa il lavoro:

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

Ho affrontato lo stesso problema ed ecco la soluzione che ho implementato con 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 
9

Per quanto riguarda la mia osservazione va, alcuna risposta postato qui può avere inaspettati effetti collaterali.

In ambiente ES2015-compatibile, è possibile evitare qualsiasi effetto collaterale utilizzando 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 
+1

Sto leggendo questo nel 2018, e questo è sicuramente il modo più pulito. – Romain

0

versione tipografico di risposta @justin, ES6 compatibile, utilizzando simboli per evitare collisioni chiave e ha aggiunto nella Object.id globale per convenienza. Basta copiare e incollare il codice qui sotto, o inserirlo in un file ObjecId.ts che importerai.

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

utilizzo nel codice ts:

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