Re: Detecting the "type" of a DOM object

On Jun 6, 2012, at 11:59 AM, Boris Zbarsky wrote:

> There's a common problem where someone has an object and they would like to tell whether it's a Node (or an Array or a Date or whatever).  Right now, the way to do that is to use instanceof.... but with a huge caveat: you have to pull the Node or Array or whatnot off the right global object.

Are you sure this is really a common problem? How often do you have (application) code that actually needs to discriminate between, for example, a DOM Node and a Date?  There are, of course, method level APIs that are designed to work with any kind of object.  For example, collection APIs.  However, such APIs (should) have no or very few dependencies upon the actual kind of objects passed to them. You don't need to discriminate between a DOM Node and a Date to store either as the value of an Array element. 

Beyond the world of object agnostic APIs, such as collections, it is a poorly designed API that interchangeably accepts  specific kinds of specialized behaviorally unrelated objects and then takes differing actions based upon discriminating the actual "kind" of object. Putting this a bit more concretely.   Can you really think of a case where you would want to have something like:

function computeSomeCoherentAbstractValue(dateOrNode) {
       if (determineIfDate(dateOrNode) return computeMeaningfulResultForDate(dateOrNode)
       else if (determineIfNode) return computeMeaningfulResultForNode(dateOrNode)
       else throw new Error("Wrong kind of object");
}

Is there really some abstract computation that applicable only to Dates and DOM Nodes?  If you find yourself writing code something like this, you should probably step back and think more abstractly about your design.  How is it that either a DOM Node or a Date could arrive at this stage of a computation. 

Note, that I'm not saying that there is never a need to polymorphiclly accept differing kinds of specialized objects.  But it usually occurs in contexts where there is some behavioral commonality among the objects. I just don't think situations like Node or Date are (or should be) at all common.  So it shouldn't provide the basis for considering  how to handle situations where there is an actual need for polymorphism. 

We also have the issue of what we mean by "is a".  Are we talking about behavioral equivalence or are you talking about identical implementation.  Equating class-based implementation inheritance with behavioral equivalence has generally been found to be too constraining.    The problems with a different Node or Array being associated with different globals is an example the the issues that arise when you equate "is a" testing with implementation details. "is a" should not mean "instanceof".  Even if your favorite language had a rational subclass-based definition of instanceof (which JS arguably does not have)

Languages like Java have the concept of interfaces in order to avoid some of these problems. Interfaces allow for multiple independent implementations of a common behavioral  abstraction.  WebIDL is an interface specification language.  We will unnecessarily constrain both implementations and client code if we assume there is a single implementation of each interface or otherwise equate interface equivalence with implementation equivalence.

We could have an WebIDL classification interface, that exposes a test method

  interface A IsA {
      boolean isAWebIDL(DOMString interfaceName);
  };

that may be implemented.  However, in my experience, such general purpose classification APIs are an attractive nuisance that get frequent misused. They are seldom really needed.  Behavioral probing is usually sufficient for  any necessary interface based behavioral classification. For example, in JS you might define:

function isDomNode (obj) {
   return 'nodeType' in obj;
}

or if you wanted to be a bit more precise, 

function isDomNode (obj) {
   return 'nodeType' in obj && 'compareDocumentPosition' in obj
}
 
But even here, why would somebody do this classification?  It's back to the Date or Node questions.  It seems more likely that they would want to dispatch on the value of nodeType or some other characteristic value.  But, you might ask, how do they know whether an object even has a nodeType? Just access it and see. If an non-Node object has reached a point in the code where you expect an object that behaves like a Node you already have a bug.  Just let the code break so you find it.


Allen

Received on Thursday, 21 June 2012 18:15:32 UTC