Related
I need to do some experiment and I need to know some kind of unique identifier for objects in javascript, so I can see if they are the same. I don't want to use equality operators, I need something like the id() function in python.
Does something like this exist ?
Update My original answer below was written 6 years ago in a style befitting the times and my understanding. In response to some conversation in the comments, a more modern approach to this is as follows:
(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`
If you have archaic browser requirements, check here for browser compatibility for Object.defineProperty.
The original answer is kept below (instead of just in the change history) because I think the comparison is valuable.
You can give the following a spin. This also gives you the option to explicitly set an object's ID in its constructor or elsewhere.
(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());
Take care to make sure that whatever member you use to internally store the unique ID doesn't collide with another automatically created member name.
So far as my observation goes, any answer posted here can have unexpected side effects.
In ES2015-compatible enviroment, you can avoid any side effects by using 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
Latest browsers provide a cleaner method for extending Object.prototype. This code will make the property hidden from property enumeration (for p in o)
For the browsers that implement defineProperty, you can implement uniqueId property like this:
(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;
}
});
}());
For details, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Actually, you don't need to modify the object prototype and add a function there. The following should work well for your purpose.
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;
}
For browsers implementing the Object.defineProperty() method, the code below generates and returns a function that you can bind to any object you own.
This approach has the advantage of not extending Object.prototype.
The code works by checking if the given object has a __objectID__ property, and by defining it as a hidden (non-enumerable) read-only property if not.
So it is safe against any attempt to change or redefine the read-only obj.__objectID__ property after it has been defined, and consistently throws a nice error instead of silently fail.
Finally, in the quite extreme case where some other code would already have defined __objectID__ on a given object, this value would simply be returned.
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__;
}
};
})();
Typescript version of #justin answer, ES6 compatible, using Symbols to prevent any key collision and added into the global Object.id for convenience. Just copy paste the code below, or put it into an ObjecId.ts file you will import.
(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];
};
}
Example of usage:
console.log(Object.id(myObject));
jQuery code uses it's own data() method as such id.
var id = $.data(object);
At the backstage method data creates a very special field in object called "jQuery" + now() put there next id of a stream of unique ids like
id = elem[ expando ] = ++uuid;
I'd suggest you use the same method as John Resig obviously knows all there is about JavaScript and his method is based on all that knowledge.
For the purpose of comparing two objects, the simplest way to do this would be to add a unique property to one of the objects at the time you need to compare the objects, check if the property exists in the other and then remove it again. This saves overriding prototypes.
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
I faced the same problem and here's the solution I implemented with 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
I've used code like this, which will cause Objects to stringify with unique strings:
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__ + ']';
};
the __proto__ bits are to keep the __id__ getter from showing up in the object. this has been only tested in firefox.
Notwithstanding the advice not to modify Object.prototype, this can still be really useful for testing, within a limited scope. The author of the accepted answer changed it, but is still setting Object.id, which doesn't make sense to me. Here's a snippet that does the job:
// 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
This one will calculate a HashCode for each object, optimized for string, number and virtually anything that has a getHashCode function. For the rest it assigns a new reference number.
(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++);
}
})();
If you came here because you deal with class instances like me you can use static vars/methods to reference instances by a custom unique 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 );
}
Here's a variant of Justin Johnson's answer that provides a scalability benefit when you are creating billions of objects for which you want the ID.
Specifically, rather than solely using a 1-up counter (that might overflow the representational limits of Number, and can't be cycled without risking reusing an ID), we register the object and its newly generated ID with a FinalizationRegistry, such that, at some point after the object is garbage collected, the ID is returned to a freelist for reuse by a newly created object (Python's id function can also return the same ID for multiple objects, so long as the existence of the two objects does not overlap in time).
Limitations:
It only works on objects, not JS primitives (this is somewhat reasonable; unlike Python, where everything is an object, JS primitives typically aren't, and the id function logically only works on objects, since primitives need not "exist" in any reasonably identifiable way).
If the code creates (without discarding) billions of objects, asks for their IDs, then releases them all at once and never asks for an ID again, the recovered IDs in the freelist constitute a memory leak of sorts. Hopefully the JS optimizer stores them efficiently, so the cost remains a small fraction of what the objects themselves cost, but it's still a cost. In cases where objects with IDs are regularly created and destroyed, the wasted memory is roughly tied to the maximum number of such ID-ed objects in existence at any given point in time.
If those limitations aren't a problem though, this works fairly well. I modified the testing code a bit to hand control back to the event loop (and hopefully the garbage collector) now and again while creating 10M garbage objects to ID, and on my browser, nearly half the object IDs get reclaimed for reuse; the final loop making five objects and IDing them produces IDs just above 1M, when over 2M objects had IDs generated at some point. In a realistic scenario with meaningful code executing and real async usage I'd expect better results simply because there would be more opportunities for the finalization registry to perform cleanup.
async function sleep(ms) {
await _sleep(ms);
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(function() {
if ( typeof Object.id != "undefined" ) return;
var freelist = []; // Stores previously used IDs for reuse when an object with
// an ID is garbage collected, so creating and dropping billions
// of objects doesn't consume all available IDs
const registry = new FinalizationRegistry((freeid) => {
freelist.push(freeid);
});
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: freelist.length ? freelist.pop() : ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
registry.register(o, o.__uniqueid); // Sometime after o is collected, its ID
// will be reclaimed for use by a new object
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(/./));
var idsum = 0; // So we do something real to prevent optimizing out code
// Make a ton of temporary objects with IDs, handing control back to the event loop
// every once in a while to (hopefully) see some IDs returned to the pool
for (var i = 0; i < 1000000; ++i) {
idsum += Object.id({c: i});
}
sleep(10).then(() => {
console.log(Object.id(function() { console.log("Hey"); }));
for (var i = 1000000; i < 2000000; ++i) {
idsum += Object.id({c: i});
}
console.log(Object.id(function() { console.log("There"); }));
sleep(10).then(() => {
for (var i = 0; i < 5; ++i) {
console.log(Object.id([i]));
}
console.log(idsum);
});
});
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
This might sound like a newbie question (and probably is) however I am not sure how I should work with this problem:
I create an object like this (js & jquery):
var data = [{ "groupName": "G1", "status": "active"}, { "groupName": "G2", "status": "inactive"}, { "groupName": "G3", "status": "active"}]
function game() {
this.groups = [];
this.makeGroups = function(group) {
this.groups.push(group);
}
this.getGroupsWithStatus = function (status) {
return data.filter( group => group.status == status )
}
}
function group(key) {
this.groupKey = key;
this.showGroupName = function() {
return this.groupKey
}
this.checkPreviousGroup = function () {
var filtered = myGame.getGroupsWithStatus("active"); // 4. calling instance by its name - is it good?
var self = this; // 2. making reference to parent object
var prevGroup;
$.each(filtered, function(group, key) { // 3. filtering with each - is it necessery?
if ( self.groupKey == group.key ) {
prevGroup = key - 1;
return;
}
})
if ( prevGroup.length ) {
return prevGroup
} else {
return false
}
}
}
var myGame = new game();
var groups = myGame.groups // 1. global shortcut to groups
var group1 = new group(0);
var group2 = new group(1);
var group3 = new group(2);
This code should work, however I am almost sure that it is written in a manner it shouldn't be.
Here are my possible problems.
I create shortcut groups to myGame.groups. I've been told that I make a global variable this way (and I suppose I want to) but it is possibly dangerous. Is it?
I use var self = this so I can have a reference to parent object in $.each(). Is there any other way to call parent object in the function referring to another "this"?
I use here $.each to test the expression. Javascript filter function is shorter and better, more readable, but it tests ALL elements and I can break it. In other words if I have 5 elements and I want to filter all elements before 3rd element that has status == "active" is there other way to do it?
I make only one instance of game class called myGame. Should I call it by it's name from other classes or should I do it in other way (i.e. find all instances of game class and assign it to variable?)
Kalreg.
I am attempting to make a simple immutable Interval type. It has a start and an end property. Is there a straightforward way to utilize the immutable-js library to create your own custom immutable types?
My current attempt at using a containment pattern with Immutable.Map and Immutable.List is not working so well. difference, for example, should return an immutable list of Intervals, but because it creates new instances of Interval, the List it returns will not pass equality.
It feels like I am taking the wrong approach, but I'm not sure how. I could easily represent the data with Immutable.Map, but I want to include difference in its public interface, and not include general Map methods like get and set. i.e. I want it to be immutable, yet be its own type with its own interface.
var Immutable = require('immutable')
function Interval(start, end) {
this.value = Immutable.Map({
start: start,
end: end
})
}
Interval.prototype.start = function () {
return this.value.get('start')
}
Interval.prototype.end = function () {
return this.value.get('end')
}
Interval.prototype.equals = function(interval) {
return this.value.equals(interval.value)
}
Interval.prototype.difference = function (subtrahend) {
var nonoverlapping = subtrahend.start() > this.end() || subtrahend.end() < this.start()
return nonoverlapping ? Immutable.List.of(this) :
Immutable.List.of(
new Interval(this.start(), subtrahend.start()),
new Interval(subtrahend.end(), this.end())
)
.filter(function(interval) {
return interval.end() > interval.start()
})
}
Some tests:
it('should return an empty array for an exact difference', function() {
var interval = new Interval(80, 90)
interval.difference(interval)
.equals(Immutable.List())
.should.be.true
})
it('should return an array with the same Interval for a missed difference', function() {
var interval = new Interval(80, 90)
console.log(interval.difference(new Interval(50, 60)).toJS());
interval.difference(new Interval(50, 60))
.equals(Immutable.List.of([
{ value: Immutable.Map({ start: 80, end: 90 }) }
]))
.should.be.true
})
Why not use Record type:
var Interval = Immutable.Record({start: 0, end: 1})
This creates the record type for you, defining start and end properties with default values of 0 and 1, respectively.
Now to create instances of Interval, just do:
var newInterval = new Interval() //will have default values above
var newInterval = new Interval({start: 10, end: 30)
Note that instances of Record type are just value types, so they should just contain data. You can, of course, define functions that operate on this data value.
var difference = function (interval1, interval2) {
var nonoverlapping = interval2.start > interval1.end || interval2.end < interval1.start
return nonoverlapping ? Immutable.List.of(interval1) :
Immutable.List([
new Interval({start: interval1.start, end: interval2.start}),
new Interval({start: interval2.end, end: interval1.end})
])
.filter(function(interval) {
return interval.end > interval.start
});
}
Equality you get for free, using Immutable.is(interval1, interval2)
For more information on the record type: http://facebook.github.io/immutable-js/docs/#/Record
You can use "extendable-immutable" library.
Then it is easy:
const {Map} = require('extendable-immutable');
class Interval extends Map {
constructor(start, end) {
super({
value: new Map({start: start, end: end})
});
}
start() {
return this.get('value').get('start');
}
end() {
return this.get('value').get('end');
}
//...
}
I do not recommend Record if you choose methods (such as start, end) over fields like me. If you implement it with Record you will have field(s) that are exposed automatically.
Instead of exposing a value field, you may want to create your Record custom class with _value field, however it is not always possible. See:
https://github.com/facebook/immutable-js/issues/970
Implement the method valueOf.
For example:
Interval.prototype.valueOf = function() {
return Map({ class: Interval, value: this.value });
}
and on the tests:
Immutable.is(new Interval(80, 90), new Interval(80, 90)); // true
Another option is to convert the class to a ValueObject by implementing the methods equals and hashCode (Use the functions Immutable.is and Immutable.hash for the implementation).
I am using a complex object graph serialized to JSON with MVC4/jQuery/Sammy/Rivets for SPA functionality.
I have a object graph that looks a bit like this when serialized to JSON (obviously mocked-up):
model =
{
Name: "Me",
Age: 22,
Hobbies:
[
{ Name: "Biking", IsActive: true },
{ Name: "Programming", IsActive: true }
]
}
Everything works quite well until I need Unobtrusive validation, since my Hobbies are in a SlickGrid and I am managing all the data myself. To handle this I am returning my ModelState with my JSON next to my model.
return JSON(new { model = model, modelState = this.ModelState });
From there I intend to iterate through the modelState and assign errors to the right place with some custom function, but there is one problem.
ModelState looks like this:
"Name",
"Age",
"Hobbies[0].Name",
"Hobbies[0].IsActive",
"Hobbies[1].Name",
"Hobbies[1].IsActive"
I need to separate the [0]'s into an object and [1]'s into their own objects so I can smoothly get the values. This gets confusing for me when I begin to account for a third level of complex object array.
Solution:
var ModelStateConverter = function ($, module) {
module = module || {};
// Convert The ModelState form style object to a standard JS object structure.
module.toObject = function (modelState) {
var ModelState = {};
$.each(modelState, function (key, value) {
AssignValuesToObjectStore(key, ModelState, value);
});
return ModelState;
}
// item is the full identifier ex. "Hobbies[0].Name"
// store is the object we are going to throw arrays, objects, and values into.
// value is the error message we want to get in the right place.
// index is an internal processing parameter for arrays only, setting it's value has no effect.
function AssignValuesToObjectStore(item, store, value, index) {
var periodMatch = item.match(/[\.]/);
if (periodMatch === null) {
if (Array.isArray(store)) {
if (store[index] === undefined) {
store[index] = {};
}
store[index][item] = value;
}
else {
store[item] = value;
}
}
else {
// This wasn't a simple property or end of chain.
var currentProperty = item.slice(0, periodMatch.index); // Get our property name up to the first period.
var container = {}; // We assume we are dealing with an object unless proven to be an array.
var arrayIndex; // This is irrelevant unless we have an array.
if (currentProperty.slice(-1, currentProperty.length) === "]") {
// We are dealing with an array! Hoo Ray?!
arrayIndex = parseInt(currentProperty.slice(currentProperty.indexOf("[") + 1, currentProperty.indexOf("]")));
currentProperty = currentProperty.slice(0, currentProperty.indexOf("[")); // remove the indexer ex. [0] so we are left with the real name
container = []; // We know we need an array instead;
}
if (store[currentProperty] === undefined) {
store[currentProperty] = container; // If this property isn't already created, then do so now.
}
//Recurseive nature here.
AssignValuesToObjectStore(item.slice(periodMatch.index + 1, item.length), store[currentProperty], value, arrayIndex);
}
}
return module;
}($, ModelStateConverter);
You can call this from:
ModelStateConverter.toObject(data.modelState);
Where data.modelState is assumed to be the ModelState from the server.
You could try a library like JSON.NET, or the class JavaScriptSerializer, to serialize the ModelState.
I need to do some experiment and I need to know some kind of unique identifier for objects in javascript, so I can see if they are the same. I don't want to use equality operators, I need something like the id() function in python.
Does something like this exist ?
Update My original answer below was written 6 years ago in a style befitting the times and my understanding. In response to some conversation in the comments, a more modern approach to this is as follows:
(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`
If you have archaic browser requirements, check here for browser compatibility for Object.defineProperty.
The original answer is kept below (instead of just in the change history) because I think the comparison is valuable.
You can give the following a spin. This also gives you the option to explicitly set an object's ID in its constructor or elsewhere.
(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());
Take care to make sure that whatever member you use to internally store the unique ID doesn't collide with another automatically created member name.
So far as my observation goes, any answer posted here can have unexpected side effects.
In ES2015-compatible enviroment, you can avoid any side effects by using 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
Latest browsers provide a cleaner method for extending Object.prototype. This code will make the property hidden from property enumeration (for p in o)
For the browsers that implement defineProperty, you can implement uniqueId property like this:
(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;
}
});
}());
For details, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Actually, you don't need to modify the object prototype and add a function there. The following should work well for your purpose.
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;
}
For browsers implementing the Object.defineProperty() method, the code below generates and returns a function that you can bind to any object you own.
This approach has the advantage of not extending Object.prototype.
The code works by checking if the given object has a __objectID__ property, and by defining it as a hidden (non-enumerable) read-only property if not.
So it is safe against any attempt to change or redefine the read-only obj.__objectID__ property after it has been defined, and consistently throws a nice error instead of silently fail.
Finally, in the quite extreme case where some other code would already have defined __objectID__ on a given object, this value would simply be returned.
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__;
}
};
})();
Typescript version of #justin answer, ES6 compatible, using Symbols to prevent any key collision and added into the global Object.id for convenience. Just copy paste the code below, or put it into an ObjecId.ts file you will import.
(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];
};
}
Example of usage:
console.log(Object.id(myObject));
jQuery code uses it's own data() method as such id.
var id = $.data(object);
At the backstage method data creates a very special field in object called "jQuery" + now() put there next id of a stream of unique ids like
id = elem[ expando ] = ++uuid;
I'd suggest you use the same method as John Resig obviously knows all there is about JavaScript and his method is based on all that knowledge.
For the purpose of comparing two objects, the simplest way to do this would be to add a unique property to one of the objects at the time you need to compare the objects, check if the property exists in the other and then remove it again. This saves overriding prototypes.
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
I faced the same problem and here's the solution I implemented with 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
I've used code like this, which will cause Objects to stringify with unique strings:
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__ + ']';
};
the __proto__ bits are to keep the __id__ getter from showing up in the object. this has been only tested in firefox.
Notwithstanding the advice not to modify Object.prototype, this can still be really useful for testing, within a limited scope. The author of the accepted answer changed it, but is still setting Object.id, which doesn't make sense to me. Here's a snippet that does the job:
// 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
This one will calculate a HashCode for each object, optimized for string, number and virtually anything that has a getHashCode function. For the rest it assigns a new reference number.
(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++);
}
})();
If you came here because you deal with class instances like me you can use static vars/methods to reference instances by a custom unique 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 );
}
Here's a variant of Justin Johnson's answer that provides a scalability benefit when you are creating billions of objects for which you want the ID.
Specifically, rather than solely using a 1-up counter (that might overflow the representational limits of Number, and can't be cycled without risking reusing an ID), we register the object and its newly generated ID with a FinalizationRegistry, such that, at some point after the object is garbage collected, the ID is returned to a freelist for reuse by a newly created object (Python's id function can also return the same ID for multiple objects, so long as the existence of the two objects does not overlap in time).
Limitations:
It only works on objects, not JS primitives (this is somewhat reasonable; unlike Python, where everything is an object, JS primitives typically aren't, and the id function logically only works on objects, since primitives need not "exist" in any reasonably identifiable way).
If the code creates (without discarding) billions of objects, asks for their IDs, then releases them all at once and never asks for an ID again, the recovered IDs in the freelist constitute a memory leak of sorts. Hopefully the JS optimizer stores them efficiently, so the cost remains a small fraction of what the objects themselves cost, but it's still a cost. In cases where objects with IDs are regularly created and destroyed, the wasted memory is roughly tied to the maximum number of such ID-ed objects in existence at any given point in time.
If those limitations aren't a problem though, this works fairly well. I modified the testing code a bit to hand control back to the event loop (and hopefully the garbage collector) now and again while creating 10M garbage objects to ID, and on my browser, nearly half the object IDs get reclaimed for reuse; the final loop making five objects and IDing them produces IDs just above 1M, when over 2M objects had IDs generated at some point. In a realistic scenario with meaningful code executing and real async usage I'd expect better results simply because there would be more opportunities for the finalization registry to perform cleanup.
async function sleep(ms) {
await _sleep(ms);
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(function() {
if ( typeof Object.id != "undefined" ) return;
var freelist = []; // Stores previously used IDs for reuse when an object with
// an ID is garbage collected, so creating and dropping billions
// of objects doesn't consume all available IDs
const registry = new FinalizationRegistry((freeid) => {
freelist.push(freeid);
});
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: freelist.length ? freelist.pop() : ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
registry.register(o, o.__uniqueid); // Sometime after o is collected, its ID
// will be reclaimed for use by a new object
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(/./));
var idsum = 0; // So we do something real to prevent optimizing out code
// Make a ton of temporary objects with IDs, handing control back to the event loop
// every once in a while to (hopefully) see some IDs returned to the pool
for (var i = 0; i < 1000000; ++i) {
idsum += Object.id({c: i});
}
sleep(10).then(() => {
console.log(Object.id(function() { console.log("Hey"); }));
for (var i = 1000000; i < 2000000; ++i) {
idsum += Object.id({c: i});
}
console.log(Object.id(function() { console.log("There"); }));
sleep(10).then(() => {
for (var i = 0; i < 5; ++i) {
console.log(Object.id([i]));
}
console.log(idsum);
});
});
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`