3

I'm writing a class and want the instances to be comparable by <, >, ==.

For < and >, valueOf works fine.

== is different, however, but I want to have that as well. I could easily implement an isEqual method, but that's just not the same.

My current solution is a cache for all the created objects:

const cache = {}

class Comparable {
  constructor (id) {
    if (cache[id]) return cache[id]

    cache[id] = this
  }
}

That way, the comparison works. Unfortunately, this also blocks the garbage collector.

Is there another way of enabling new Comparable(42) == new Comparable(42), that does not impede GC?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • 1
    I'm afraid it's not possible with JS. This links might be helpful: [Override the Equivalence Comparison in Javascript](https://stackoverflow.com/questions/10539938/override-the-equivalence-comparison-in-javascript), [Fake operator overloading in JavaScript](http://2ality.com/2011/12/fake-operator-overloading.html) – Serhii Holinei Feb 02 '19 at 12:13
  • Garbage collector do not collect your objects because they are held as legitimate references inside “cache” array. It simply do not know that object is not need anymore. If your code allows, you may implement some kind of “dispose()” method to remove object from cache when it is not need anymore (or wipe entire cache in the point, where it is not need anymore). – Serge Ageyev Feb 02 '19 at 13:20
  • 1
    @SergeyGoliney That's what I thought, just wanted to make sure I wasn't overlooking anything. Thank you for your input – rasenplanscher Feb 04 '19 at 10:44
  • @SergeAgeyev I am aware of that fact, which is why I specifically asked for a way around that. Any method calls that flush the cache or remove the objects in question would automatically break the comparability, which is why I will not implement such. For Example: `const x = new Comparable(1); const y = new Comparable(1); x.destroy(); const z = new Comparable(1);` – at this point, `y` and `z` would exist somewhere in the running application and `y === z` would be false, although it should be true. – rasenplanscher Feb 04 '19 at 10:48

2 Answers2

2

You seem to be looking for hash consing, but as you experienced this cannot be implemented efficiently since JavaScript does not (yet) support weak (or soft) references.

No, it is not possible to overwrite any operators, including ==, in JS. == will always compare two objects by reference, there's nothing you can do about it. Your best bet will be creating a compare method.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

First of all keep in mind that == is always worse than === because of such troubles. When you use x == y comparison where both operands are objects then js will compare them as objects. (more here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) It is easy to test just running

class ComparableObject {
  constructor(id) {
    this.id = id;
  }

  valueOf() {
    console.log("log", this.id);
    return this.id;
  }
}

new ComparableObject("12") == new ComparableObject(12);

It will not produce any log but this:

new ComparableObject("12") == new ComparableObject(12).valueOf();

will print:

log 12
log 12
true

There are few solutions for your need:

Number(new ComparableObject("12")) == Number(new ComparableObject(12));
new ComparableObject("12").valueOf() == new ComparableObject(12).valueOf();

GC cannot do something untill cache object will not remove references to instances.

  • Thank you for your input. Unfortunately, the information here did not advance me toward a better solution than mine. I am fully aware of the differences between `==` and `===`. I chose `==` here because if that worked through some kind of trickery while `===` would still check strict object identity, that would be great. I had not originally made that explicit in order to keep my question focussed. – rasenplanscher Feb 04 '19 at 10:39