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`
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`
Is it possible to create an array that will only allow objects of a certain to be stored in it? Is there a method that adds an element to the array I can override?
Yes you can, just override the push array of the array (let's say all you want to store are numbers than do the following:
var myArr = [];
myArr.push = function(){
for(var arg of arguments) {
if(arg.constructor == Number) Array.prototype.push.call(this, arg);
}
}
Simply change Number to whatever constructor you want to match. Also I would probably add and else statement or something, to throw an error if that's what you want.
UPDATE:
Using Object.observe (currently only available in chrome):
var myArr = [];
Array.observe(myArr, function(changes) {
for(var change of changes) {
if(change.type == "update") {
if(myArr[change.name].constructor !== Number) myArr.splice(change.name, 1);
} else if(change.type == 'splice') {
if(change.addedCount > 0) {
if(myArr[change.index].constructor !== Number) myArr.splice(change.index, 1);
}
}
}
});
Now in ES6 there are proxies which you should be able to do the following:
var myArr = new Proxy([], {
set(obj, prop, value) {
if(value.constructor !== Number) {
obj.splice(prop, 1);
}
//I belive thats it, there's probably more to it, yet because I don't use firefox or IE Technical preview I can't really tell you.
}
});
Not directly. But you can hide the array in a closure and only provide your custom API to access it:
var myArray = (function() {
var array = [];
return {
set: function(index, value) {
/* Check if value is allowed */
array[index] = value;
},
get: function(index) {
return array[index];
}
};
})();
Use it like
myArray.set(123, 'abc');
myArray.get(123); // 'abc' (assuming it was allowed)
In javascript using an object parameter is my preferred way of working with functions. To check that a function has the required parameters I either (Solution 1) loop through all the object parameters properties and throw an error or (Solution 2) wait until a required property is needed and throw an error. Solution two seems efficient but I have to throws in multiple places in the function. Solution 1 seems pragmatic but should probably be a reusable piece of code. Is there another solution I should be looking at?
You can actually do this
var propsNeeded = ["prop1", "prop2", "blah", "blah", "blah"],
obj = {
prop1: "Hi"
}
function hasRequiredProperties(props, obj){
return Object.keys(obj).sort().join() == propsNeeded.sort().join();
}
console.log(hasRequiredProperties(propsNeeded, obj)); // false
You can check for single properties like
function hasProperty(propName, obj){
return obj.hasOwnProperty(propName);
}
For consistency I would create require method and use it always when some property is required.
var require = function (key, object) {
if (typeof object[key] === 'undefined') {
throw new Error('Required property ' + key + ' is undefined');
}
};
I would test if required property exists as soon as I'm certain that property is needed. Like this:
var example = function (args) {
require('alwaysRequired', args);
// some code here which uses property alwaysRequired
if (args.something) {
require('sometimesRequired', args);
// some code here which uses property sometimesRequired
}
};
Using #Amit's answer I'd probably add a method to Object itself:
Object.prototype.hasAllProperties = function(props, fire){
var result = Object.keys(this).sort().join() == propsNeeded.sort().join();
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
and in your function:
function someFunction(myObject){
var objComplete = myObject.hasAllProperties(["prop1", "prop2", "prop3"], false);
}
Update:
After noticing the problem with #Amit's original answer, here's what I suggest:
Object.prototype.hasAllProperties = function(props, fire){
var result = true;
$(props).each(function(i, e){
if (!this.hasOwnProperty(e) ) {
result = false;
return false;
}
});
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
This is just a general case of checking for presence of keys on a object, which can be done easily enough with
requiredParams.every(function(prop) { return prop in paramObj; })
It almost reads like natural language. "Taking the required parameters, is EVERY one of them IN the parameter object?".
Just wrap this in function checkParams(paramObj, requiredParams) for easy re-use.
More generally, this is the problem of asking if one list (in this case the list of required parameters) is included in another list (the keys on the params object). So we can write a general routine for list inclusion:
function listIncluded(list1, list2) {
return list1.every(function(e) { return list2.indexOf(e) !== -1; });
}
Then our parameter-checking becomes
function checkParams(paramObj, requiredParams) {
return listIncluded(requiredParams, Object.keys(paramObj));
}
If you want to know if object has at least some properties you can use this function without third parameter:
function hasRequiredProperties(propsNeeded, obj, strict) {
if (strict) return Object.keys(obj).sort().join() == propsNeeded.sort().join();
for (var i in propsNeeded ) {
if (!obj.hasOwnProperty(propsNeeded[i])) return false;
}
return true;
};
Example:
options = {url: {
protocol: 'https:',
hostname: 'encrypted.google.com',
port: '80'
}
};
propsNeeded = ['protocol', 'hostname'];
hasRequiredProperties(propsNeeded, options.url); // true
hasRequiredProperties(propsNeeded, options.url, true); // false
I am looking for an efficient way to translate my Ember object to a json string, to use it in a websocket message below
/*
* Model
*/
App.node = Ember.Object.extend({
name: 'theName',
type: 'theType',
value: 'theValue',
})
The websocket method:
App.io.emit('node', {node: hash});
hash should be the json representation of the node. {name: thename, type: theType, ..}
There must be a fast onliner to do this.. I dont want to do it manualy since i have many attributes and they are likely to change..
As stated you can take inspiration from the ember-runtime/lib/core.js#inspect function to get the keys of an object, see http://jsfiddle.net/pangratz666/UUusD/
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, ret = [];
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
} // ignore useless items
if (Ember.typeOf(v) === 'function') {
continue;
}
ret.push(key);
}
}
return this.getProperties.apply(this, ret);
}
});
Note, since commit 1124005 - which is available in ember-latest.js and in the next release - you can pass the ret array directly to getProperties, so the return statement of the getJson function looks like this:
return this.getProperties(ret);
You can get a plain JS object (or hash) from an Ember.Object instance by calling getProperties() with a list of keys.
If you want it as a string, you can use JSON.stringify().
For example:
var obj = Ember.Object.create({firstName: 'Erik', lastName: 'Bryn', login: 'ebryn'}),
hash = obj.getProperties('firstName', 'lastName'), // => {firstName: 'Erik', lastName: 'Bryn'}
stringHash = JSON.stringify(hash); // => '{"firstName": "Erik", "lastName": "Bryn"}'
I have also been struggling with this. As Mirko says, if you pass the ember object to JSON.stringify you will get circular reference error. However if you store the object inside one property and use stringify on that object, it works, even nested subproperties.
var node = Ember.Object.create({
data: {
name: 'theName',
type: 'theType',
value: 'theValue'
}
});
console.log(JSON.stringify(node.get('data')));
However, this only works in Chrome, Safari and Firefox. In IE8 I get a stack overflow so this isn't a viable solution.
I have resorted to creating JSON schemas over my object models and written a recursive function to iterate over the objects using the properties in the schemas and then construct pure Javascript objects which I can then stringify and send to my server. I also use the schemas for validation so this solution works pretty well for me but if you have very large and dynamic data models this isn't possible. I'm also interested in simpler ways to accomplish this.
I modifed #pangratz solution slightly to make it handle nested hierarchies of Jsonables:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
App.io.emit('node', {node: node.toJSON()});
Or if you have an ID property and want to include it:
App.io.emit('node', {node: node.toJSON({includeId: true})});
Will this work for you?
var json = JSON.stringify( Ember.getMeta( App.node, 'values') );
The false is optional, but would be more performant if you do not intend to modify any of the properties, which is the case according to your question. This works for me, but I am wary that Ember.meta is a private method and may work differently or not even be available in future releases. (Although, it isn't immediately clear to me if Ember.getMeta() is private). You can view it in its latest source form here:
https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/utils.js
The values property contains only 'normal' properties. You can collect any cached, computed properties from Ember.meta( App.node, false ).cached. So, provided you use jQuery with your build, you can easily merge these two objects like so:
$.extend( {}, Ember.getMeta(App.node, 'values'), Ember.getMeta(App.node, 'cache') );
Sadly, I haven't found a way to get sub-structures like array properties in this manner.
I've written an extensive article on how you can convert ember models into native objects or JSON which may help you or others :)
http://pixelchild.com.au/post/44614363941/how-to-convert-ember-objects-to-json
http://byronsalau.com/blog/convert-ember-objects-to-json/
I modified #Kevin-pauli solution to make it works with arrays as well:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === 'array') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (Ember.typeOf(v) === 'array') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
I also made some further modification to get the best of both worlds. With the following version I check if the Jsonable object has a specific property that informs me on which of its properties should be serialized:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, base, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === 'array') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
if (!Ember.isNone(this.get('jsonProperties'))) {
// the object has a selective list of properties to inspect
base = this.getProperties(this.get('jsonProperties'));
} else {
// no list given: let's use all the properties
base = this;
}
for (var key in base) {
if (base.hasOwnProperty(key)) {
v = base[key];
if (v === 'toString') {
continue;
}
if (Ember.typeOf(v) === 'function') {
continue;
}
if (Ember.typeOf(v) === 'array') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
I am using this little tweak and I am happy with it. I hope it'll help others as well!
Thanks to #pangratz and #Kevin-Pauli for their solution!
Here I take #leo, #pangratz and #kevin-pauli solution a little step further. Now it iterates not only with arrays but also through has many relationships, it doesn't check if a value has the type Array but it calls the isArray function defined in Ember's API.
Coffeescript
App.Jsonable = Em.Mixin.create
getJson: ->
jsonValue = (attr) ->
return attr.map(jsonValue) if Em.isArray(attr)
return attr.getJson() if App.Jsonable.detect(attr)
attr
base =
if Em.isNone(#get('jsonProperties'))
# no list given: let's use all the properties
this
else
# the object has a selective list of properties to inspect
#getProperties(#get('jsonProperties'))
hash = {}
for own key, value of base
continue if value is 'toString' or Em.typeOf(value) is 'function'
json[key] = jsonValue(value)
json
Javascript
var hasProp = {}.hasOwnProperty;
App.Jsonable = Em.Mixin.create({
getJson: function() {
var base, hash, hashValue, key, value;
jsonValue = function(attr) {
if (Em.isArray(attr)) {
return attr.map(jsonValue);
}
if (App.Jsonable.detect(attr)) {
return attr.getJson();
}
return attr;
};
base = Em.isNone(this.get('jsonProperties')) ? this : this.getProperties(this.get('jsonProperties'));
json = {};
for (key in base) {
if (!hasProp.call(base, key)) continue;
value = base[key];
if (value === 'toString' || Em.typeOf(value) === 'function') {
continue;
}
json[key] = jsonValue(value);
}
return json;
}
});
Ember Data Model's object counts with a toJSON method which optionally receives an plain object with includeId property used to convert an Ember Data Model into a JSON with the properties of the model.
https://api.emberjs.com/ember-data/2.10/classes/DS.Model/methods/toJSON?anchor=toJSON
You can use it as follows:
const objects = models.map((model) => model.toJSON({ includeId: true }));
Hope it helps. Enjoy!
I have:
fixed and simplified code
added circular reference prevention
added use of get of value
removed all of the default properties of an empty component
//Modified by Shimon Doodkin
//Based on answers of: #leo, #pangratz, #kevin-pauli, #Klaus
//http://stackoverflow.com/questions/8669340
App.Jsonable = Em.Mixin.create({
getJson : function (keysToSkip, visited) {
//getJson() called with no arguments,
// they are to pass on values during recursion.
if (!keysToSkip)
keysToSkip = Object.keys(Ember.Component.create());
if (!visited)
visited = [];
visited.push(this);
var getIsFunction;
var jsonValue = function (attr, key, obj) {
if (Em.isArray(attr))
return attr.map(jsonValue);
if (App.Jsonable.detect(attr))
return attr.getJson(keysToSkip, visited);
return getIsFunction?obj.get(key):attr;
};
var base;
if (!Em.isNone(this.get('jsonProperties')))
base = this.getProperties(this.get('jsonProperties'));
else
base = this;
getIsFunction=Em.typeOf(base.get) === 'function';
var json = {};
var hasProp = Object.prototype.hasOwnProperty;
for (var key in base) {
if (!hasProp.call(base, key) || keysToSkip.indexOf(key) != -1)
continue;
var value = base[key];
// there are usual circular references
// on keys: ownerView, controller, context === base
if ( value === base ||
value === 'toString' ||
Em.typeOf(value) === 'function')
continue;
// optional, works also without this,
// the rule above if value === base covers the usual case
if (visited.indexOf(value) != -1)
continue;
json[key] = jsonValue(value, key, base);
}
visited.pop();
return json;
}
});
/*
example:
DeliveryInfoInput = Ember.Object.extend(App.Jsonable,{
jsonProperties: ["title","value","name"], //Optionally specify properties for json
title:"",
value:"",
input:false,
textarea:false,
size:22,
rows:"",
name:"",
hint:""
})
*/
Ember.js appears to have a JSON library available. I hopped into a console (Firebug) on one the Todos example and the following worked for me:
hash = { test:4 }
JSON.stringify(hash)
So you should be able to just change your line to
App.io.emit('node', { node:JSON.stringify(hash) })
I'm having trouble figuring out how I can take a string of an object name and check if that object actually exists.
What I'm trying to accomplish is have an array the defines the required objects for a particular JavaScript "module" to work, for instance:
var requiredImports = ['MyApp.Object1', 'MyApp.Object2'];
Then using requiredImports, I want to loop over them and check if the are defined. Without using the above array, I can do the following which is what I'm trying to accomplish:
if (MyApp.Object1 == undefined) {
alert('Missing MyApp.Object1');
}
But using the above, I'd have to hard code this for every module rather than making a generic method that I can just pass it an array of strings and have it effectively do the same check for me.
I tried doing this by just passing it the objects themselves such as:
var requiredImports = [MyApp.Object1, MyApp.Object2];
But that throws a JavaScript error when those objects do not exist, which is what I'm trying to catch.
var MyApp = {
Object1: {}
};
function exists(varName, scope) {
var parent = scope || window;
try {
varName.split('.').forEach(function (name) {
if (parent[name] === undefined) {
throw 'undefined';
}
parent = parent[name];
});
}
catch (ex) {
return false;
}
return true;
}
console.log(
exists('MyApp.Object1'), // true
exists('MyApp.Object2'), // false
exists('window'), // true
exists('document'), // true
exists('window.document') // true
);
// or
console.log(
['MyApp.Object1', 'MyApp.Object2', 'window', 'document', 'window.document'].filter(function (varName) {
return !exists(varName);
})
);
// => ["MyApp.Object2"]
Note: that forEach is ES5 and as such not implemented in some browsers. But if you'd go with this solution, there is a nice polyfill here.
You can check for definedness with
if ( typeof window['MyApp'] === 'undefined' ||
typeof window['MyApp']['Object1'] === 'undefined' )
{
alert('Missing MyApp.Object1');
}
and so on.
Assuming MyApp.Object1 is a global scope, window is the parent object and since that is the top level object, you don't need to prefix your global vars with it. So window.MyApp.Object1 is the same as MyApp.Object1 (again, assuming this is within global scope).
Also, in javascript, MyApp['Object1'] is the same as MyApp.Object1. So if we apply this principle to the main window object, you can check for window['MyApp'] or window['MyApp']['Object1'] and the key here is that you can replace 'MyApp' and 'Object1' with a variable.
Example:
/* check if a variable/object exists in the global scope) */
function checkIfExists(someVar) {
if (typeof(window[someVar]) == 'undefined')
return true;
return false;
}
var foo = 'bar';
alert(checkIfExists('foo'));
You can evaluate your custom expression in JavaScript. Consider the code below:
var MyApp = {
Object1: "foo",
Object2: "bar"
};
var IsExists = function(varName) {
return new Function('return typeof(' + varName + ') === "undefined" ? false : true;')();
};
USAGE
var requiredImports = ['MyApp.Object1', 'MyApp.Object2'];
for (var i = 0; i < requiredImports.length; i++)
{
alert(requiredImports[i] + ": " + IsExists(requiredImports[i]))
}
You only get error for first level (MyApp in your example). I assume you have only a few first-level requires, so check them manually by window[x] which does not throw:
var requiredTopLevel = ['MyApp'];
for (var i = 0; i < requiredTopLevel.length; ++i) {
if ("undefined" === typeof window[requiredTopLevel[i]]) {
// problem with requiredTopLevel[i]
}
}
and then, to check nested requires (if top-level is present) you can use the values without fear. For example this will work:
var requiredNested = { 'Object1':MyApp.Object1, 'Object2':Myapp.Object2 };
for (var name in requiredNested) {
if ("undefined" === typeof requiredNested[name]) {
// problem with name
}
}