1

I have an object where the keys are WebSockets (from the Node.JS ws library).

Say I have two different WebSockets ("Socket A" and "Socket B").

socketa === socketb // => false

Socket A is the only key in the object:

theObject.hasOwnProperty(socketa) // => true

Here's the problem:

theObject.hasOwnProperty(socketb) // => true

Even though these are two different objects (that !== each other), they are both keys in the object which only Socket A is a key of.

Same result with key in object:

socketa in theObject // => true
socketb in theObject // => true

There definitely is only one member in the object:

Object.keys(theObject).length // => 1

What's going on here?

JJJollyjim
  • 5,837
  • 19
  • 56
  • 78

3 Answers3

2

When you assign a property to an object, the key for the property is a string. If you pass something like a socket as the key, then javascript will endeavor to convert it to a string and store that as a key. If the WebSocket has no special support for the toString() method which does the conversion to a string, then you will get some generic conversion such as [object Object] which will mean that all your WebSockets will appear as the same property.

The same is true for .hasOwnProperty() when you go to look up the key. So, you will essentially be doing:

theObject.hasOwnProperty("[object Object]")

for all your sockets and thus they will all look the same.

You can work around it by giving your sockets some sort of unique ID and then using that as the key and you can put the socket itself as the data in your map object.

For example, you could do this:

function addSocketToMap(socket, obj) {
    if (!socket.uniqueKey) {
        socket.uniqueKey = addSocketToMap.cntr++;
    }
    obj[socket.uniqueKey] = socket;
}
addSocketToMap.cntr = 0;

function isSocketInMap(socket, obj) {
    if (socket.uniqueKey) {
        return obj.hasOwnProperty(socket.uniqueKey);
    }
    return false;
}

var theObject = {};
addSocketToMap(socketA, theObject);
addSocketToMap(socketB, theObject);

var a = isSocketInMap(socketA, theObject);   // true
var b = isSocketInMap(socketB, theObject);   // true

You could also probably just override toString for the WebSocket object if this doesn't cause any problems with its implementation:

 (function() {
     var cntr = 0;
     WebSocket.prototype.toString = function () {
         // add a uniqueKey if it doesn't already exist
         if (!this.uniqueKey) {
             this.uniqueKey = cntr++;
         }
         // make a unique string identifier
         // that will look like "WebSocket_xxx"
         return "WebSocket_" + this.uniqueKey;
     }
 })();
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Hmm, okay. Could I extend the prototype of WebSocket to make `toString` return some sort of special ID? – JJJollyjim Nov 10 '13 at 04:30
  • @JJ56 - you probably could, but since I don't know whether it's safe to modify your WebSocket objects, I've suggested an alternative implementation that just adds a unique property to them. See the additions to my answer. – jfriend00 Nov 10 '13 at 04:31
  • Great, thanks! I'll play around with using `toString` (then the rest of my code could stay the same, I believe), and use your solution if required. – JJJollyjim Nov 10 '13 at 04:33
  • @JJ56 - just remember that `toString(`) will have to generate a unique and repeatable string for each `WebSocket` object (meaning you will probably have to store whatever string you generate in the socket like I did above). – jfriend00 Nov 10 '13 at 04:34
  • Sure. Your system of using a counter would probably work well. – JJJollyjim Nov 10 '13 at 04:35
  • @JJ56 - I added a `toString()` override to my answer. – jfriend00 Nov 10 '13 at 04:40
  • Drat, I'd just implemented it myself :P. Luckily, it works perfectly. Thanks for all the help! – JJJollyjim Nov 10 '13 at 04:45
0

The hasOwnProperty() method takes a property name. If the WebSocket objects convert to string the same way, they would both satisfy hasOwnProperty(propName). So the property name is in the object in both cases.

Edit:

MDN reference for the in operator.
MDN reference for the hasOwnProperty() method

Both ways of testing for the property will use the property name as a string though hasOwnProperty() does not check the prototype change the way in does.

jbindel
  • 5,535
  • 2
  • 25
  • 38
  • Makes sense. However, my code is still using "in", still with the same result. – JJJollyjim Nov 10 '13 at 04:26
  • Using `in` also converts the object to a string. Using `propName in` is the same situation as using `hasOwnProperty(propName)`. In both cases, the value is converted to a string. – jbindel Nov 10 '13 at 16:15
0

hasOwnProperty takes a string argument, the name of the property. You're passing the socket, which is not what you want. It should be defined like {"socket": socketa}, then test for theobject1["socket"] === theobject2["socket"]

willy
  • 1,462
  • 11
  • 12