3

I've been using the same JavaScript library I've built for years, and now I'm encountering errors with this function:

IsArray : function ()
{
    if (typeof arguments[0] == 'object')
    {
        var criterion = arguments[0].constructor.toString().match(/array/i);
        return (criterion != null);
    }

    return false;
}

There are times when it is called when the following error is thrown:

TypeError: Cannot call method "toString" of undefined

I added the following prior to defining the criterion variable to remedy the issue:

if (arguments[0].constructor == null || arguments[0].constructor == undefined)
    return false;

However, I'd like to understand how or why this would happen. I don't know why a variable that has a type of 'object' would not have a constructor. I've never seen it prior to this issue. And what bothers me about this is this all started a few weeks after I updated another library function that checks for nulls and empty strings to try to filter out empty arrays (when comparing an empty array to an empty string, it was returning a match).

Michael McCauley
  • 853
  • 1
  • 12
  • 37

2 Answers2

1

Not all objects have a .constructor property, e.g. Object.create(null), just like not all objects have a toString method.

For your IsArray function, you should use the native Array.isArray method, or its usual polyfill.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

Well... As you may know, JavaScript is a bit surprising sometimes. With ES3, it was not easy at all to determine if an array was actually an array. So if you want to keep your legacy code, I think you should follow the great tips given by Douglas Crockford in JavaScript: The Good Parts.

JavaScript does not have a good mechanism for distinguishing between arrays and objects. We can work around that deficiency by defining our own is_array function:

var is_array = function (value) {
  return value && typeof value === 'object' && value.constructor === Array;
};

Unfortunately, it fails to identify arrays that were constructed in a different window or frame. If we want to accurately detect those foreign arrays, we have to work a little harder:

var is_array = function (value) {
  return Object.prototype.toString.apply(value) === '[object Array]';
};

Moreover, you should be very careful when you play with null because Object.create(null) creates an object without any prototype, typeof null returns object and null == undefined returns true...

With ES5, the best solution is to use Array.isArray().

Badacadabra
  • 8,043
  • 7
  • 28
  • 49
  • 1
    ES5 does have `Array.isArray`. It was only in the olden days that it was really hard. – Bergi Apr 25 '17 at 17:50
  • Nice comment, thanks! You are right. For some reasons, my mind has mixed three specifications on this topic... I thought that `Array.isArray()` was part of ES6 like `Array.from()`, but it is part of ES5. The above code was useful in ES3 and older polyfills. I will edit my answer. :) – Badacadabra Apr 25 '17 at 18:04
  • I had to test this, as I'm working with a server-side JavaScript engine, but it does the trick. My method had worked consistently for years, so I'm glad I can use this now. Odd thing, it worked with one array defined like ["","",""], and then failed a couple lines later with another similar array. So strange. – Michael McCauley May 02 '17 at 14:55
  • @Badacadabra Why would someone use Object.create(null)? – Eduard Aug 22 '18 at 11:46