0

I want to produce a hash derived from an Object's reference, and then associate the hash string to the object itself into a dictionary.

I have heard of object-hash and attempted the following implementation:

const objectHash = require('object-hash');


class A {

    a: string;
    constructor() {
        this.a = '1';
    }
}


let a = new A();
let ab = new A();

console.log(objectHash(a)); //     712041d72943c4794bb23d1d455e17b3a4ea17f5
console.log(objectHash(ab));//     712041d72943c4794bb23d1d455e17b3a4ea17f5

In despite of a and ab being different objects (although instances of the same class), the hash produced is equal, which is not the expected result. That is because the library hashes object values rather than object reference.

Expected result

let foo = new A();
let bar = new A();

const fooH = objectHash(foo); <-- Produces a unique hash string representing the reference of "foo"
const barH = objectHash(bar); <-- Produces a unique hash string representing the reference of "bar"


let objects = {
    fooH: foo,
    barH: bar 
}

This could probably be achieved in C++ or C# by using pointers and addresses, but what about Javascript? If anybody can guide me on the right path to incapsulate an object's reference into a string, it would be appreciated.

Constantin
  • 848
  • 8
  • 23
  • 1
    You cannot *get* the reference, so you cannot hash it. If you explain what your goal is, perhaps we can offer suitable alternatives. – VLAZ Nov 16 '21 at 20:17
  • It's difficult to do this in JavaScript code, because there's no access to the internal reference data. Also, objects can move around during garbage collection, so anything based on the object address would need a hook into the GC to rehash. – Barmar Nov 16 '21 at 20:17
  • @VLAZ I want to index each unique instance of `A` into an `object` dictionary as in the last piece of code found in the question – Constantin Nov 16 '21 at 20:18
  • @Barmar would the only solution be a say "GUID" generated in the object's constructor and use that as an unique identifier? Is there no low-level implementation? – Constantin Nov 16 '21 at 20:19
  • In fact, I was just about to post an answer suggesting a GUID like that. – Barmar Nov 16 '21 at 20:21
  • 2
    @Constantin you can already use a Map or a WeakMap and use the object as the key. That gives you unique key-value pairs, since objects are unique. However, it's a bit useless, since you need the object to get the object. Not too dissimilar with the hashes - you need the object to hash to get an object out of the map. So, again, if you explain what the goal is, perhaps there is a different solution. You seem to be describing [an XY problem](https://meta.stackexchange.com/questions/346503/what-is-the-opposite-of-the-xy-problem) right now. – VLAZ Nov 16 '21 at 20:21
  • @Barmar It is really not going to work in my case as I cannot inherit the class "A" which come sfrom an external library. Specifically uWebSockets – Constantin Nov 16 '21 at 20:21
  • @Barmar why a GUID over Symbol? – VLAZ Nov 16 '21 at 20:22
  • @VLAZ If there is any way to do something like `let foo = A(); let objs = {}; obs[foo] ="anyvaue";` then it actually solves my issue. The reason I was looking for the hash is because I guess you cannot index an `Object` – Constantin Nov 16 '21 at 20:23
  • @VLAZ When you log a GUID you can see the difference. – Barmar Nov 16 '21 at 20:23
  • 1
    @Constantin `new Map().set(foo, "anyvalue")`? – VLAZ Nov 16 '21 at 20:25
  • @VLAZ I was not aware of Map. It actually solves my problem. Cheers for that! – Constantin Nov 16 '21 at 20:25
  • 1
    @Constantin "*I guess you cannot index an Object*" not if you use a plain object. But maps can have arbitrary keys, including objects. – VLAZ Nov 16 '21 at 20:25
  • 1
    ...so, for future reference, that is the value of not focusing on the Y in XY problems. – VLAZ Nov 16 '21 at 20:27
  • You're right about the "XY" problem, as I was trying to solve a problem by asking a Y question where I thought Y would solve the X problem. But if you think it's doable, post an answer and I'll accept it! @VLAZ – Constantin Nov 16 '21 at 20:27

1 Answers1

2

It is not possible to get an object's reference in JavaScript in order to calculate a hash of it. The reference is an internal value for the engine that is not normally exposed.

However, if you want to index some data against an object, you can use a Map, since the keys can be any arbitrary JavaScript value, thus allowing objects. Since objects are unique during comparison, that means that the same object returns the value:

class A {
    constructor() {
        this.a = '1';
    }
}

const foo = new A();
const bar = new A();

const map = new Map()
  .set(foo, "hello")
  .set(bar, "world");

console.log(map.get(foo), map.get(bar));

JavaScript also has a WeakMap - it only allows objects as keys but in turn it only holds a weak reference to them. This means that once any other reference to the key is gone, the WeakMap will not prevent the object from being garbage collected.

Maps, and especially WeakMaps, are a viable way to extend foreign objects without actually having to nest them in your own hierarchy. Here is a quick demo

import {a, b} from 'foreign';

const extension = new WeakMap()

extension.set(a, { 
    myProperty1: true,
    myProperty2: 40,
    myProperty3: "hello",
});

extension.set(b, { 
    myProperty1: false,
    myProperty2: 2,
    myProperty3: "world"
});

Here we get some foreign objects a, and b we do not control and extend them with our own properties without ever needing to change or clone them. Looking the object in the WeakMap gives the extended properties.

See a more detailed description by Benjamin Gruenbaum


For further reference a Set and a WeakSet exhibit the same properties as a Map and WeakMap, respectively, in terms of storing objects. However, they without an extra value for the object. That can be useful, for example, to keep track of seen objects. Which event handlers have fired, or which nodes in a graph have been visited, and similar.

VLAZ
  • 26,331
  • 9
  • 49
  • 67