0

Often while using JavaScript you run into silly problems. One such problems is discerning between Object types.

Is there a way to create a function with this functionality? See Below:

_discern = function () { [ function code ] };

_discern({}); // Logs: Object

_discern([]); // Logs: Array

_discern(document); // Logs: Pseudo-Object

_discern(document.querySelectorAll("*")); // Logs: Pseudo-Array

I've already tried creating a function that checks for array-likeness, but that didn't work as good as I had hoped:

isArrLike = function (_) {

    _[0] = 0; return [].slice.call(_).length >= Object.values(_).length;
};

And I've tried using that behavior into another function. All failures. Is there a way?

imaxwill
  • 113
  • 8
  • How do you define "pseudo-object"? Do you mean [*exotic object*](http://www.ecma-international.org/ecma-262/7.0/index.html#sec-exotic-object)? There was a concept of [*host object*](http://www.ecma-international.org/ecma-262/5.1/index.html#sec-4.3.8), but it's been removed. A good start would be to provide an explicit definition of what tests or features define each type of object. – RobG May 29 '17 at 04:08

3 Answers3

1

Looks like you want to know if something behaves like an array, instead of looking for the actual type/constructor.

If that's the case, it should be enough to check if something is iterable:

function isIterable(obj) {
  // checks for null and undefined
  if (obj == null) {
    return false;
  }
  return typeof obj[Symbol.iterator] === 'function';
}
Logain
  • 4,259
  • 1
  • 23
  • 32
  • You're amazing. – imaxwill May 29 '17 at 04:21
  • What is your definition of "iterable"? Array methods work on any object with a *length* property, is that enough? Is a NodeList an "iterable"? It certainly **is** iterable. ;-) – RobG May 29 '17 at 04:23
  • @RobG Array methods DO NOT work on any object with a length property. E.g. arguments object of function. It has a length property and properties indexed from zero, but it doesn't have Array's built-in methods like forEach() and map() – Experimenter Aug 08 '19 at 12:05
  • @Experimenter— `Array.prototype.call({length:1}, x => console.log(x))` works for me. And `Array.prototype.forEach.call({length:1, 0:'blah'}, x => console.log(x))`. – RobG Aug 08 '19 at 20:46
0

All of these are objects:

  • is array → return x instanceof Array or Array.isArray(x)
  • is function → return typeof x === 'function'
  • is HTML element → return x instanceof HTMLElement
  • is plain object → return typeof x === 'object' && /* ... is not array, not function, not HTML element */
Taufik Nurrohman
  • 3,329
  • 24
  • 39
  • `x instanceof Array` will fail across frames, `x instanceof HTMLElement` makes assumptions about the host environment that aren't necessarily supported, as does `typeof x === 'object'` not being an HTML element or some other host object (try them in IE). – RobG May 29 '17 at 04:18
0

Thanks to Logain's answer, I can solve the problem. Here's my approach:

kind = function (a) {

    let u = toString.call(a.valueOf()).slice(8, -1);

    if (a == null || u == "String" || u == "Number" || u == "Boolean") {

        return u;
    }

    else if (typeof a[Symbol.iterator] == "function") {

        return u != "Array" ? "Array-Like" : u;
    }

    else {

        return u;
    }
};
imaxwill
  • 113
  • 8