Overload a function in javascript?

So I had this little javascript method that I was using to hide stuff on my web page. (This version is slightly modified – hopefully I didn’t add too many bugs):


    function hide(arg) {
        if((arg instanceof Array) || (arg instanceof HTMLCollection))
            var elemList = arg;
        else
            var elemList = [ arg ];

        for(var i = 0; i < elemList.length; ++i)
            elemList[i].style.display = 'none';
    }

Somtimes I was passing single DOM elements, but other times I was passing a list of them either as an Array or as an HTMLCollection. Two usages were as follows:


    hide(document.getElementById("thatThing"))
    hide(document.getElementsByName("thoseThings"))

This worked great for a while, but apparently the powers-that-be decided the storage class HTMLCollection just wasn’t cutting it and calls to getElementsByName one day started returning a new data type: NodeList, which of course broke my script.

I don’t have much experience with Javascript (or any other weakly typed language for that matter). It occurs to me that what I’m really trying to do is come up with a substitute for the technique of function overloading that folks use in Java or C++ (strongly typed languages). In those languages I would have two methods doSomething – one that took a list, and the other that took a single instance. And in those languages, you wouldn’t have the return type of core library methods change on you.

So my questions are:

  1. Should I just be abandoning this technique of interrogating argument types to achieve what is basically a function overload? Should I instead restrict myself to having two methods: hide() and hideList() (which seems rather cumbersome given my background)?

  2. Or is there some more robust way I could have written my original hide method such that it wouldn’t have failed and that is in general a better way to achieve function overloading?

Thanks much,
Matt

Nothing wrong with using that approach to function overloading in JavaScript.

The only issue that you have there is that you have three different sorts of input but only test for two of them.

Since you want the processing for arrays and nodelists to be the same and for single values to be different you could test the one thing that distinguishes them - both arrays and nodelists have a length property while single entries do not. You are not using any of the other properties and methods that arrays have but nodelists don’t and so do not need to distinguish between an array and a nodelist.

Dear Stephen,

Thankyou for your response.

If there’s nothing wrong with the approach, then why was my script working one day, and not the next? If I can’t trust that the data types returned from core methods won’t change on me, then testing for these types seems unwise.

As I said, document.getElementsByName() wasn’t returning a NodeList before. I had no need to check for it. Should I also check for instanceof ThingyList in case it starts returning a ThingyList next week?

Can I be assured they will never add a length property to a DOM document element? I would have put more faith in the stability of the data type returned by getElementsByName than in an unchanging property list of a DOM document element.

If testing the data types of arguments to effect function overloading is standard programming practice, then IMHO they shouldn’t be changing the return types of core system methods.

I’m left annoyed and faithless.

Thanks,
Matt

I have no idea why it was working before. document.getElementsByName has always been supposed to return a nodelist according to the standards. I have never come across any instance where it has returned an array - unless you convert it to an array first - but then it wouldn’t update automatically if the web page changed. As for HTMLcollection - that has never een a part of the JavaScript standard - it may have been something proprietary to a specific browser back before the DOM 1 standard was produced. I would possibly expect that to be the type for document.images or document.links which are collections that have been around since before the DOM.

You can rely reasonably well on modern browsers following the JavaScript standards so calls that are supposed to return a nodelist wlil always return a nodelist. The code you had probably only worked in specific older browsers that recognised what an HTMLcollection was.

The reason why I suggested testing if it has a length is that anything that has a length has more than one element in it and you need a loop to process them. You’d effectively be testing whether what is received is one or more than one regardless of its type.

Even if you were using a language such as Java that allows proper overloading, your code still would have broken. Imagine this Java pseudo-code.

void hide(Array arg) {
    // ...
}

void hide(HTMLCollection arg) {
    // ...
}

void hide(HTMLElement arg) {
    // ...
}

Now imagine that you suddenly start trying to pass a NodeList to the hide method. It wouldn’t work. So your emulation of overloading is fine, because even proper overloading would have broken. You were simply the unfortunate victim of a changing DOM API. In an ideal world, all those collection types would extend a super type so you could automatically catch any sub types that might arise in the future, but alas that isn’t the API we have to work with. One option available to you would be to reverse the order that you test arg’s type. Start by testing if arg is a single element, and if so, array wrap it. Else, assume that arg is array-like.

Stephen,

Really! This script was written months ago. It’s not yet published on my website, and I haven’t yet gotten around to testing it on any browser than my firefox browser that’s always been set for auto-updates.

This thing was definitely working before, but you sound like you know what you’re talking about. I’m befuddled. I’m tempted to try to roll-back my firefox browser version to see if I can’t figure out why it was working.

Thanks,
Matt

https://developer.mozilla.org/en-US/docs/DOM/element.getElementsByTagName

Note: While the W3C DOM 3 Core specification says elements is a NodeList that was simply because of a an attempt to have the “core” specification not depend on the “html” specification at that time. The DOM 4 draft says that elements is an HTMLCollection.

Gecko/Firefox currently returns a NodeList (Bug 162927) but starting with Gecko/Firefox 19, this method will return HTMLCollection (Bug 799464). Internet Explorer returns a HTMLCollection. WebKit returns a NodeList. Opera also returns a NodeList, but with a namedItem method implemented, which makes it similar to a HTMLCollection.

Jeff,

When I wrote that statement I was thinking that getElementsByName had changed it’s return type. I was thinking that maybe some standards group or some software group that writes the firefox browser thought it was ok to return a different container type since javascript itself is loosely typed: “Who’s going to care whether it’s an HTMLCollection or a NodeList so long as the new type has all the same properties…”

The idea of someone changing the return type of a core function in a strongly typed language is ludicrous because it’s pretty much guaranteed to break every bit of code on the planet that uses it.

But perhaps it’s just as far fetched in javascript. Stephen thinks I must be mistaken and that there is some other explanation for what happened. Perhaps he is right. It’s been months since I’ve looked at this script. I’m sure it was working. But perhaps it is something as simple as my last working version not getting saved. I really can’t be sure at this point.

Thanks,
Matt

Jeff,

Ah Ha!

So the return type IS changing around! I have multiple computers in the house. Perhaps one of them was running an older version of firefox. Or perhaps I got an auto-update. Maybe I didn’t muff this after all.

Now tell me again the last time any method in the Java standard library changed one of it’s return types? I’d be very surprised if that ever happened.

I’m returning to my previous state of being annoyed and faithless.

Thanks very very much,
Matt

Stephen,

Coincidentally, I recently (accidentally) passed a string to a python method that normally took a list. A string is a funny data type that is perhaps most often processed as an atomic entity, but it can also be thought of as an array of characters. And it does have a length. So my function happily processed it. The results were quite frightful.

So looking for the length property wouldn’t work to distinguish between an array of strings and a single string. A minor point, but perhaps worth mentioning.

Matt

The getElementsByClassName method appears to be the only method that doesn’t return the same data type in all browsers - all the others are either a NodeList or an HTMLCollection across all browsers according to everything I can find that discusses this.

An HTMLCollection is basically the same as a NodeList but with a few extra methods available. So the simplest solution would be to simply test for either one and treat them the same unless you are going to use the additional methods - even there you’d be safest to still test for either one as some browsers also implement the methods on both so that they are effectively the same thing in those browsers and older browsers will not necessarily have the new HTMLCollection methods.

You can use my overload implementation!

regards XD