Problems with Workarounds

While using objects as sets and maps works okay in simple situations, the approach can get more complicated once you run into the limitations of object properties. For example, since all object properties must be strings, you must be certain no two keys evaluate to the same string. Consider the following:

  1. let map = Object.create(null);
  2. map[5] = "foo";
  3. console.log(map["5"]); // "foo"

This example assigns the string value "foo" to a numeric key of 5. Internally, that numeric value is converted to a string, so map["5"] and map[5] actually reference the same property. That internal conversion can cause problems when you want to use both numbers and strings as keys. Another problem arises when using objects as keys, like this:

  1. let map = Object.create(null),
  2. key1 = {},
  3. key2 = {};
  4. map[key1] = "foo";
  5. console.log(map[key2]); // "foo"

Here, map[key2] and map[key1] reference the same value. The objects key1 and key2 are converted to strings because object properties must be strings. Since "[object Object]" is the default string representation for objects, both key1 and key2 are converted to that string. This can cause errors that may not be obvious because it’s logical to assume that different object keys would, in fact, be different.

The conversion to the default string representation makes it difficult to use objects as keys. (The same problem exists when trying to use an object as a set.)

Maps with a key whose value is falsy present their own particular problem, too. A falsy value is automatically converted to false when used in situations where a boolean value is required, such as in the condition of an if statement. This conversion alone isn’t a problem—so long as you’re careful as to how you use values. For instance, look at this code:

  1. let map = Object.create(null);
  2. map.count = 1;
  3. // checking for the existence of "count" or a nonzero value?
  4. if (map.count) {
  5. // ...
  6. }

This example has some ambiguity as to how map.count should be used. Is the if statement intended to check for the existence of map.count or that the value is nonzero? The code inside the if statement will execute because the value 1 is truthy. However, if map.count is 0, or if map.count doesn’t exist, the code inside the if statement would not be executed.

These are difficult problems to identify and debug when they occur in large applications, which is a prime reason that ECMAScript 6 adds both sets and maps to the language.

I> JavaScript has the in operator that returns true if a property exists in an object without reading the value of the object. However, the in operator also searches the prototype of an object, which makes it only safe to use when an object has a null prototype. Even so, many developers still incorrectly use code as in the last example rather than using in.