Breaking a lance for JavaScript interfaces

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Dormilich
    Recognized Expert Expert
    • Aug 2008
    • 8694

    Breaking a lance for JavaScript interfaces

    (short article)

    We often come across situations, where we need to check, whether a passed object is of a certain type. for standard objects (1) it is quite common to use the typeof operator for such tests. Objects coming from the document tree (2) are usually tested through their nodeType property.

    A common field, where this is necessary, are functions or methods, that require a function as parameter, namely addEventListene r(), call() and apply().
    Code:
    // this is a necessary test if [I]fn[/I] is passed as parameter
    if ("function" != typeof fn)
    {
        throw new Error("Supplied argument is not a function.");
    }
    this.addEventListener("event", fn, false);
    Using interfaces, you can rewrite that as
    Code:
    // this is a necessary test if [I]fn[/I] is passed as parameter
    if (!(fn instanceof Function))
    {
        throw new Error("Supplied argument is not a function.");
    }
    this.addEventListener("event", fn, false);
    admittedly, there is not that much advantage of instanceof over typeof, but we have not yet touched DOM.

    Suppose you are walking through the document tree, looking for an element.
    Code:
    while ("undefined" != typeof obj)
    {
        // do something with obj
        obj = obj.parentNode;
    }
    Are you sure that after the last object "undefined" is returned? wouldn’t be a "as long as obj is an element" be a better condition? (yes, it would)
    Code:
    while (obj instanceof Element)
    {
        // do something with obj
        obj = obj.parentNode;
        // the parent node of an element is always another element*
    }
    Interfaces also posses some very useful properties from objects – inheritance. that is, an interface can match a number of different objects, as long as they have the required properties*.
    Code:
    // match all text nodes 
    if (obj instanceof Text)
    {
        var text = obj.data;
    }
    We see here the basic use: I want to use a property named data (return the text of a text node), the literal approach to use it without error is "if there is a data property, access it".

    We can’t use the following, because this tests not for the property, but for the content of the property.
    Code:
    if (obj.data)
    we could use
    Code:
    if ("data" in obj)
    but that’s essentially the same as
    Code:
    if (obj instanceof CharacterData)
    because the data property is part of the CharacterData interface, from which the Text interface inherits. So why don’t I use CharacterData? Because comment nodes also inherit from that and I don’t want comment text this time.

    Personally, I find that way more descriptive than
    Code:
    // match all text nodes 
    if (3 == obj.nodeType || 4 == obj.nodeType)
    {
        var text = obj.data;
    }
    * the parentNode property is part of the Element interface (well, originally from the Node interface, but Element extends Node).
    ** methods count as properties too, got something to do with the object implementation
Working...