From CommonJS Spec Wiki
Jump to: navigation, search


In JavaScript there are both "javascriptish/rhinocerous", native patterns for collection types embodied by the usage and generic methods of Array and Object. However these objects and their patterns have several disadvantages which give rise to the need for "best practices" in user-defined collections.

  • .length assignment is not observable in pure-JS interoperably
  • [index] assignment is only special for Array and inheriting Array does not imbue types with their specialness.
  • [name] assignment can have name collisions between names in an object's prototype chain and those that it ought to contain. Assigning arbitrary strings in this domain can potentially mask builtins.
  • The in operator cannot be safely used on Object instances to check for containment or iteration. You have to use the Object.prototype.hasOwnProperty.call method instead.
  • Proxies for native objects cannot observe indexing or length assignment.
  • Array's constructor is not strictly variadic. That is, creating an Array with a single value that is a number must be done with Array literals. Array and Object do not support copy construction.
  • Object can only be used for mappings from a domain of strings.

However, there are distinct advantages of playing nicely with these types, like being able to use the Array.prototype generic methods. For this reason, there need to be two layers of collection types: those that play well with natives ("rhinocerous types", if you will), and those that can be manipulated in pure JavaScript using a new set of conventional names. With these user-defined types, it's necessary to use methods for all interaction with the underlying collection.

We can create a system of base types that support mutual copy construction and mutual interface implementation. That is, all collections should be constructable by passing an iterable as their argument, and in turn all collections should be iterable. Python does this well, but we can do one step better by making the default iteration on dictionary mappings to render an iterable of (key, value) pairs instead of simply the keys. We can also define orthogonal interfaces intrinsic to each of the three main collection types and cross implement those interfaces in each other type to the extent it make sense. That is, a List is a duck-type for a Dict where the keys are offsets. A Dict is a Set of pairs. A Set is an unordered List.

Methods intrinsic to:

  • Set (unordered values): insert, remove, retrieve, discard
  • Dict (unordered pairs): get, set, del, has, put, cut
  • List (ordered values, (index value pairs)): push, pop, shift, unshift

But List implements all of these Dict interface, and Dict can inherit Set.

This is a proposal for modules to be included in the standard library. Since there's some coupling between iteration and stream IO, I've begun layout out those modules as well. My recommendation is to implement the IO, file, and socket modules in the standard library. That would leave each platform to implement a "posix" module with all the routines that these other libraries would need.

This is very rough draft status. I imagine there's a lot of room for discussion on nomenclature and intended scope for the CommonJS standard library.