4
\$\begingroup\$

The following code is a javascript typeOf function. We aren't here to discuss coding conventions or whether or not extending a native object like I have is kosher or not. I want to know in what cases this would fail to correctly identify an object and hopefully ways to correct it. Or is this sufficient for 99% of cases?

Also, if you think of a way to make this smaller while keeping the information intact I'd be glad to hear that as well.

"use strict"
var FN = Function, 
    g = FN('return this')(); //jslint safe 'global' reference

Object.typeOf = (function (g) {
    return function (o) {
        var n = null,
            r = false,
            nodeType = {
                1:'element_node',
                2:'attribute_node',
                3:'text_node',
                4:'cdata_section_node',
                5:'entity_reference_node',
                6:'entity_node',
                7:'processing_instrction_node',
                8:'comment_node',
                9:'document_node',
                10:'document_type_node',
                11:'document_fragment_node1',
                12:'notation_node'
            },
            u;

        /*********
         *  Following if statements are direct comparisons
         *  because any additional checks on o if it equals
         *  either of these results in an error. So we check
         *  these first prior to proceeding.
         *********/

        if (o === u) {
            return 'undefined';
        }

        if (o === n) {
            return 'null';
        }

        /*********
         *  If we made it this far, just return the set the
         *  r variable and return at the end;
         *********/

        switch (o) {
        case g:
            r = 'global';
            break;
        case g.document:
            r = 'htmldocument';
            break;
        default:
            break;
        }

        /*********
         *  Originally a large switch statement changed 
         *  due to below comments.
         *********/

        if (o.nodeType && nodeType[o.nodeType]) {
            r = nodeType[o.nodeType];
        }

        /*********
         *  If r is still false, we do a standard check
         *  using the Object.toString.call method to determine
         *  its "correct" type;
         *********/

        return r || ({}).toString.call(o).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
    }
}(g));
\$\endgroup\$
2
  • \$\begingroup\$ In this changed part: if (o.nodeType && nodeType[o.nodeType]) { r = o.nodeType;}, I think you mean to use: r = nodeType[o.nodeType] \$\endgroup\$
    – jfriend00
    Commented Sep 29, 2011 at 5:49
  • \$\begingroup\$ Oops, haha good catch I've corrected it \$\endgroup\$
    – Akidi
    Commented Oct 8, 2011 at 8:20

2 Answers 2

7
\$\begingroup\$

Before evaluating something, you must first establish what the evaluation criteria are. So you must first define what "correctly identify an object" means.

The ECMAScript typeof operator returns values as specified in ECMA-262, unfortunately they are different values to the actual Type as defined in the spec. Nor does the value have much relationship to the internal class property.

Futher, host objects can return anything they like for typeof and internal class, they can even throw errors if you attempt to access them.

It is rarely necessary to know in a general sense what "type" something is, it's more common to want to know if a value meets specific criteria for the intended purpose. For example, passing a DOM element's style object to your function in Firefox returns "cssstyledeclaration", in IE it returns "object".

Does that meet your criteria for "correctly identify" (whatever they might be)? What is the caller to make of the different values? Why would it want to know?

I don't know the answers (or even the full set of questions that might be asked), so I can't work out if your function does the right thing or not.

Some comments on the code:

> var FN = Function, 
>     g = FN('return this')(); //jslint safe 'global' reference

Isn't:

var g = this;

sufficient?

> Object.typeOf = (function (g) {
>     return function (o) {

What is the point of the IIFE? There is no code run in it, all it does is add a closure to an empty (more or less) variable object of the outer function.

>    }
> }(g));

If the sole purpose was to pass in a reference to the global object, then:

    }
}(this));

will do the job. Since you aren't in strict mode, you can use the following within a normal function expression:

var global = (function(){return this;})();

the big switch statement can be replaced by something like:

if (typeof o.nodeType == 'number') {
  var nodeTypes = {1:'element_node',2:'attribute_node',3:'text_node',4:'cdata_section_node',
    5:'entity_reference_node',6:'entity_node',7:'processing_instrction_node',
    8:'comment_node',9:'document_node',10:'document_type_node',
    11:'document_fragment_node1',12:'notation_node'};
  r = nodeTypes[o.nodeType];
}
\$\endgroup\$
2
  • \$\begingroup\$ Much obliged. Note I added "use strict" & changed out the nodeType portion; does that change any of the code for global you supplied or no? \$\endgroup\$
    – Akidi
    Commented Sep 29, 2011 at 5:03
  • \$\begingroup\$ It means you can just pass this to the function rather than the global g (and threfore don't need to initialise g either). I think you need to define what objects you want to know the type of, and then specify what they are. e.g. passing an xmlHTTPRequest object to your function returns different values in different browsers (IE 8 vs Firefox). \$\endgroup\$
    – RobG
    Commented Sep 29, 2011 at 5:59
2
\$\begingroup\$

Not to be intentionally pedantic, but we are here to discuss things like coding conventions.

I find myself disliking how you switch from one style to another within the same method.

    if (o === n) {
        return 'null';
    }

    /*********
     *  If we made it this far, just return the set the
     *  r variable and return at the end;
     *********/

    switch (o) {
    case g:
        r = 'global';
        break;
    case g.document:
        r = 'htmldocument';
        break;
    default:
        break;
    }

This switch statement could be written (shorter) as two if statements (and the comments add nothing to the function).

The r variable is unnecessary (anywhere it is used can be replaced with a return statement).

The rest of the variables within that function are constants WRT it.

I would rewrite this whole thing like so:

/*jslint maxerr: 50, indent: 4 */
(function (g, Object, n, u) {
    "use strict";
    var nodeType = {
            1: 'element_node',
            2: 'attribute_node',
            3: 'text_node',
            4: 'cdata_section_node',
            5: 'entity_reference_node',
            6: 'entity_node',
            7: 'processing_instrction_node',
            8: 'comment_node',
            9: 'document_node',
            10: 'document_type_node',
            11: 'document_fragment_node1',
            12: 'notation_node'
        };
    Object.typeOf = function (o) {
        if (o === u) {
            return 'undefined';
        }

        if (o === n) {
            return 'null';
        }

        if (o === g) {
            return 'global';
        }

        if (o === g.document) {
            return 'htmldocument';
        }

        if (o.nodeType && nodeType[o.nodeType]) {
            return nodeType[o.nodeType];
        }

        return ({}).toString.call(o).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
    };
}(this, Object, null));
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.