pengenal objek unik dalam javascript

121

Saya perlu melakukan beberapa percobaan dan saya perlu mengetahui beberapa jenis pengenal unik untuk objek di javascript, jadi saya bisa melihat apakah mereka sama. Saya tidak ingin menggunakan operator kesetaraan, saya membutuhkan sesuatu seperti fungsi id () di python.

Apakah ada yang seperti ini?

Stefano Borini
sumber
2
Saya agak penasaran, mengapa Anda ingin menghindari operator persamaan?
CMS
22
karena saya ingin sesuatu yang sederhana, dan saya ingin melihat angka, sesuatu yang jelas. Bahasa ini membuat anak kucing menangis, saya sudah cukup melawannya.
Stefano Borini
4
Operator kesetaraan ketat (===) akan melakukan apa yang Anda minta pada objek (jika Anda membandingkan angka / string / dll, itu bukan hal yang sama), dan lebih sederhana daripada membangun ID unik rahasia ke setiap objek.
Ben Zotto
4
@CMS @Ben Memiliki id unik dapat berguna untuk debugging atau mengimplementasikan hal-hal seperti IdentitySet.
Alex Jasmin
9
Saya ingin menyampaikan bahwa saya telah melakukan terobosan dalam pemahaman javascript. semacam menutup sinaps. Sekarang semuanya jelas. Saya telah melihat banyak hal. Saya mendapatkan level di programmer javascript.
Stefano Borini

Jawaban:

69

Perbarui Jawaban asli saya di bawah ini ditulis 6 tahun yang lalu dengan gaya yang sesuai dengan zaman dan pemahaman saya. Menanggapi beberapa percakapan di komentar, pendekatan yang lebih modern untuk ini adalah sebagai berikut:

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

Jika Anda memiliki persyaratan browser kuno, periksa di sini untuk kompatibilitas browser untuk Object.defineProperty.

Jawaban asli disimpan di bawah (bukan hanya di riwayat perubahan) karena menurut saya perbandingan itu berharga.


Anda dapat melakukan putaran berikut. Ini juga memberi Anda opsi untuk menyetel ID objek secara eksplisit di konstruktornya atau di tempat lain.

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

Berhati-hatilah untuk memastikan bahwa anggota apa pun yang Anda gunakan untuk menyimpan ID unik secara internal tidak bertabrakan dengan nama anggota yang dibuat secara otomatis.

Justin Johnson
sumber
1
@Justin Menambahkan properti ke Object.prototype bermasalah di ECMAScript 3 karena properti ini dapat dihitung pada semua objek. Jadi jika Anda mendefinisikan Object.prototype.a maka "a" akan terlihat ketika Anda melakukannya untuk (prop in {}) alert (prop); Jadi, Anda harus membuat kompromi antara menambah Object.prototype dan mampu melakukan iterasi melalui record seperti objek menggunakan loop for..in. Ini adalah masalah serius bagi perpustakaan
Alex Jasmin
29
Tidak ada kompromi. Sudah lama dianggap sebagai praktik terbaik untuk selalu digunakan object.hasOwnProperty(member)saat menggunakan for..inloop. Ini adalah praktik yang terdokumentasi dengan baik dan yang diterapkan oleh jslint
Justin Johnson
3
tidak satu pun dari saya akan merekomendasikan untuk melakukan ini juga, setidaknya tidak untuk setiap objek, Anda dapat melakukan hal yang sama hanya dengan objek yang Anda tangani. sayangnya sebagian besar waktu kita harus menggunakan perpustakaan javascript eksternal, dan sayangnya tidak semuanya diprogram dengan baik jadi hindari ini kecuali Anda memiliki kendali penuh atas semua perpustakaan yang termasuk dalam halaman web Anda, atau setidaknya Anda tahu mereka ditangani dengan baik .
tidak berguna
1
@JustinJohnson: Ada kompromi dalam ES5: defineProperty(…, {enumerable:false}). Dan uidmetodenya sendiri harus tetap berada di Objectnamespace
Bergi
@JustinJohnson jika katakanlah id objek adalah 4bagaimana Anda kemudian menetapkan obj dengan id 4 ke variabel sehingga Anda dapat melakukan sesuatu dengannya ... seperti mengakses propertinya?
Tuan
51

Sejauh pengamatan saya, jawaban apa pun yang diposting di sini dapat memiliki efek samping yang tidak terduga.

Di lingkungan yang kompatibel dengan ES2015, Anda dapat menghindari efek samping apa pun dengan menggunakan 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
hakatashi
sumber
1
Mengapa tidak return currentId?
Gust van de Wal
Mengapa WeakMapsebagai lawan Map? Fungsi itu akan macet jika Anda memberinya string atau angka.
Nate Symer
35

Browser terbaru menyediakan metode yang lebih bersih untuk memperluas Object.prototype. Kode ini akan membuat properti disembunyikan dari enumerasi properti (untuk p in o)

Untuk browser yang mengimplementasikan defineProperty , Anda bisa mengimplementasikan properti uniqueId seperti ini:

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

Untuk detailnya, lihat https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty

Aleksandar Totic
sumber
2
"Browser terbaru" tampaknya tidak menyertakan Firefox 3.6. (Ya, saya memilih keluar dari perlombaan peningkatan di versi Firefox yang lebih baru, dan saya yakin saya bukan satu-satunya. Selain itu, FF3.6 baru berusia 1 tahun.)
bart
7
Umur 1 tahun tidak "hanya" dalam olahraga ini. Web berkembang secara dinamis - itu hal yang bagus. Browser modern memiliki pembaru otomatis untuk tujuan memungkinkan itu.
Kos
1
Beberapa tahun kemudian, ini tampaknya bekerja lebih baik bagi saya daripada jawaban yang diterima.
Fitter Man
11

Sebenarnya, Anda tidak perlu memodifikasi objectprototipe dan menambahkan fungsi di sana. Berikut ini harus bekerja dengan baik untuk tujuan Anda.

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;
}
KalEl
sumber
4
nocase, snake_case, dan camelCase telah membuat ketiganya menjadi potongan kode 6 baris. Anda tidak melihatnya setiap hari
Gust van de Wal
ini sepertinya mekanisme yang jauh lebih meyakinkan daripada objectperetasan. Anda hanya perlu menjalankannya ketika Anda ingin objek OP cocok dan tetap berada di luar jalan sepanjang waktu.
JL Peyret
6

Untuk browser yang menerapkan Object.defineProperty()metode ini, kode di bawah ini menghasilkan dan mengembalikan fungsi yang dapat Anda ikat ke objek apa pun yang Anda miliki.

Keuntungan dari pendekatan ini adalah tidak memperluas Object.prototype.

Kode bekerja dengan memeriksa apakah objek yang diberikan memiliki __objectID__properti, dan dengan mendefinisikannya sebagai properti read-only tersembunyi (non-enumerable) jika tidak.

Jadi aman terhadap segala upaya untuk mengubah atau mendefinisikan ulang obj.__objectID__properti hanya-baca setelah itu telah ditentukan, dan secara konsisten melontarkan kesalahan bagus alih-alih gagal diam-diam.

Akhirnya, dalam kasus yang sangat ekstrim di mana beberapa kode lain sudah didefinisikan __objectID__pada objek tertentu, nilai ini akan dikembalikan.

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

         }
    };

})();
Luc125
sumber
4

Kode jQuery menggunakan data()metode itu sendiri seperti id tersebut.

var id = $.data(object);

Pada metode backstage datamembuat field yang sangat khusus yang objectdisebut "jQuery" + now()put there next id dari aliran id unik seperti

id = elem[ expando ] = ++uuid;

Saya sarankan Anda menggunakan metode yang sama karena John Resig jelas tahu semua yang ada tentang JavaScript dan metodenya didasarkan pada semua pengetahuan itu.

vava
sumber
5
dataMetode jQuery memiliki kekurangan. Lihat misalnya stackoverflow.com/questions/1915341/… . Juga, John Resig sama sekali tidak tahu semua yang perlu diketahui tentang JavaScript, dan percaya bahwa dia tidak akan membantu Anda sebagai pengembang JavaScript.
Tim Down
1
@Tim, ini memiliki banyak kekurangan dalam hal mendapatkan id unik seperti metode lain yang disajikan di sini karena kira-kira sama di bawah tenda. Dan ya, saya yakin John Resig tahu lebih banyak daripada saya dan saya harus belajar dari keputusannya bahkan jika dia bukan Douglas Crockford.
vava
AFAIK $ .data tidak berfungsi pada objek JavaScript tetapi hanya elemen DOM.
mb21
4

Versi naskah ketikan jawaban @justin, kompatibel dengan ES6, menggunakan Simbol untuk mencegah benturan kunci dan ditambahkan ke Object.id global untuk kenyamanan. Cukup salin dan tempel kode di bawah ini, atau masukkan ke file ObjecId.ts yang akan Anda impor.

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

Contoh penggunaan:

console.log(Object.id(myObject));
Flavien Volken
sumber
1

Saya telah menggunakan kode seperti ini, yang akan menyebabkan Objek merangkai dengan string unik:

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

yang __proto__bit untuk menjaga __id__getter dari muncul dalam objek. ini hanya diuji di firefox.

Eric Strom
sumber
Hanya perlu diingat bahwa __defineGetter__ini tidak standar.
Tomáš Zato - Kembalikan Monica
1

Terlepas dari saran untuk tidak mengubah Object.prototype, ini masih bisa sangat berguna untuk pengujian, dalam cakupan terbatas. Penulis jawaban yang diterima mengubahnya, tetapi masih pengaturan Object.id, yang tidak masuk akal bagi saya. Berikut cuplikan yang berfungsi:

// 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
Klortho
sumber
1

Saya menghadapi masalah yang sama dan inilah solusi yang saya terapkan dengan 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 
Jaime Rios
sumber
1

Untuk tujuan membandingkan dua objek, cara termudah untuk melakukannya adalah dengan menambahkan properti unik ke salah satu objek pada saat Anda perlu membandingkan objek, periksa apakah properti tersebut ada di objek lain, lalu hapus lagi. Ini menghemat prototipe utama.

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
adevart
sumber