How to check if it's an array?

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Easy
#JavaScript #Arrays #JS Basics

Brief Answer

Checking if it’s an array can be done in several ways: Array.isArray(value) (recommended), value instanceof Array, Object.prototype.toString.call(value) === '[object Array]' or checking the constructor property. The most reliable way is Array.isArray().

Recommended methods:

  • Array.isArray() — most reliable and fastest
  • instanceof Array — works in most cases
  • toString.call() — universal for all types

What does checking if it’s an array mean

Type checking is determining whether a variable is an array. In JavaScript this is important because arrays have special behavior and methods that differ from other objects.

The problem with typeof

const arr = [1, 2, 3];
const obj = {0: 1, 1: 2, 2: 3, length: 3};
 
console.log(typeof arr); // "object" ❌ Doesn't help!
console.log(typeof obj); // "object" ❌ Same result!
 
// typeof doesn't distinguish arrays from objects
console.log(typeof null); // "object" ❌ Even null!

Array checking methods

const numbers = [1, 2, 3, 4, 5];
const notArray = {0: 1, 1: 2, length: 2};
 
console.log(Array.isArray(numbers)); // true
console.log(Array.isArray(notArray)); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray(undefined)); // false

Advantages:

  • ✅ Most reliable method
  • ✅ Fast and efficient
  • ✅ Works with arrays from other frames
  • ✅ Standard ES5 method

Disadvantages:

  • ❌ Not supported in IE8 and below

2. instanceof Array

const fruits = ["apple", "banana", "orange"];
const fakeArray = {0: "apple", length: 1};
 
console.log(fruits instanceof Array); // true
console.log(fakeArray instanceof Array); // false
console.log("string" instanceof Array); // false

Advantages:

  • ✅ Simple and clear
  • ✅ Good browser support
  • ✅ Works with inheritance

Disadvantages:

  • ❌ Problems with iframe and different contexts
  • ❌ Can be fooled by prototype changes

3. Object.prototype.toString.call()

const data = [1, 2, 3];
const notData = "not array";
 
console.log(Object.prototype.toString.call(data)); // "[object Array]"
console.log(Object.prototype.toString.call(notData)); // "[object String]"
 
// Helper function
function isArray(value) {
  return Object.prototype.toString.call(value) === '[object Array]';
}
 
console.log(isArray([1, 2, 3])); // true
console.log(isArray({})); // false

Advantages:

  • ✅ Works in all browsers
  • ✅ Context independent
  • ✅ Universal for all types

Disadvantages:

  • ❌ Longer syntax
  • ❌ Slower than other methods

4. Constructor checking

const items = [1, 2, 3, 4, 5];
 
console.log(items.constructor === Array); // true
 
// But can be fooled
const fake = {};
fake.constructor = Array;
console.log(fake.constructor === Array); // true ❌ False positive!

Disadvantages:

  • ❌ Can be changed
  • ❌ Doesn’t work with null/undefined
  • ❌ Unreliable method

5. Array methods checking

function looksLikeArray(value) {
  return value &&
         typeof value === 'object' &&
         typeof value.length === 'number' &&
         typeof value.push === 'function' &&
         typeof value.pop === 'function';
}
 
const realArray = [1, 2, 3];
const arrayLike = {length: 3, push: function() {}, pop: function() {}};
 
console.log(looksLikeArray(realArray)); // true
console.log(looksLikeArray(arrayLike)); // true ❌ False positive!

Disadvantages:

  • ❌ Can give false positives
  • ❌ Doesn’t check the real nature of the object

Performance comparison

Performance test

function performanceTest() {
  const testArray = [1, 2, 3, 4, 5];
  const iterations = 1000000;
  
  // Test 1: Array.isArray()
  console.time('Array.isArray');
  for (let i = 0; i < iterations; i++) {
    Array.isArray(testArray);
  }
  console.timeEnd('Array.isArray');
  
  // Test 2: instanceof
  console.time('instanceof');
  for (let i = 0; i < iterations; i++) {
    testArray instanceof Array;
  }
  console.timeEnd('instanceof');
  
  // Test 3: toString.call
  console.time('toString.call');
  for (let i = 0; i < iterations; i++) {
    Object.prototype.toString.call(testArray) === '[object Array]';
  }
  console.timeEnd('toString.call');
  
  // Test 4: constructor
  console.time('constructor');
  for (let i = 0; i < iterations; i++) {
    testArray.constructor === Array;
  }
  console.timeEnd('constructor');
}
 
performanceTest();

Results (approximate)

MethodTime (ms)ReliabilitySupport
Array.isArray()~10HighES5+
instanceof Array~15MediumAll browsers
toString.call()~25HighAll browsers
constructor~8LowAll browsers

Practical examples

1. Input data validation

class DataProcessor {
  constructor() {
    this.data = [];
  }
  
  // Safe data addition
  addData(input) {
    if (Array.isArray(input)) {
      this.data.push(...input); // Spread the array
    } else {
      this.data.push(input); // Add as element
    }
  }
  
  // Check and process
  processInput(value) {
    if (Array.isArray(value)) {
      return value.map(item => this.processItem(item));
    }
    return this.processItem(value);
  }
  
  processItem(item) {
    return typeof item === 'string' ? item.toUpperCase() : item;
  }
}
 
const processor = new DataProcessor();
processor.addData([1, 2, 3]); // Will add 1, 2, 3
processor.addData("single"); // Will add "single"
 
console.log(processor.data); // [1, 2, 3, "single"]

2. Universal iteration function

function forEach(collection, callback) {
  // Check if it's an array
  if (Array.isArray(collection)) {
    collection.forEach(callback);
    return;
  }
  
  // Check if it's an object with length (array-like)
  if (collection && typeof collection.length === 'number') {
    for (let i = 0; i < collection.length; i++) {
      callback(collection[i], i, collection);
    }
    return;
  }
  
  // Regular object
  if (typeof collection === 'object' && collection !== null) {
    Object.keys(collection).forEach(key => {
      callback(collection[key], key, collection);
    });
  }
}
 
// Usage
forEach([1, 2, 3], (item, index) => {
  console.log(`Array[${index}]: ${item}`);
});
 
forEach({a: 1, b: 2}, (value, key) => {
  console.log(`Object[${key}]: ${value}`);
});
 
forEach("hello", (char, index) => {
  console.log(`String[${index}]: ${char}`);
});

3. Typed data system

class TypeChecker {
  static getType(value) {
    // Check for null and undefined
    if (value === null) return 'null';
    if (value === undefined) return 'undefined';
    
    // Check for array
    if (Array.isArray(value)) return 'array';
    
    // Check for date
    if (value instanceof Date) return 'date';
    
    // Check for regular expression
    if (value instanceof RegExp) return 'regexp';
    
    // Basic types
    return typeof value;
  }
  
  static validate(value, expectedType) {
    const actualType = this.getType(value);
    
    if (actualType !== expectedType) {
      throw new Error(
        `Expected ${expectedType}, got ${actualType}`
      );
    }
    
    return true;
  }
  
  static isArrayOf(array, type) {
    if (!Array.isArray(array)) {
      return false;
    }
    
    return array.every(item => this.getType(item) === type);
  }
}
 
// Usage
try {
  TypeChecker.validate([1, 2, 3], 'array'); // ✅ Will pass
  TypeChecker.validate("string", 'array'); // ❌ Error
} catch (error) {
  console.error(error.message);
}
 
console.log(TypeChecker.isArrayOf([1, 2, 3], 'number')); // true
console.log(TypeChecker.isArrayOf([1, "2", 3], 'number')); // false

Practice tasks

Task 1

const value1 = [];
const value2 = {};
const value3 = null;
 
console.log(Array.isArray(value1));
console.log(Array.isArray(value2));
console.log(Array.isArray(value3));
Answer true, false, false — only the empty array is an array.

Task 2

const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};
 
console.log(Array.isArray(arrayLike));
console.log(arrayLike instanceof Array);
console.log(typeof arrayLike);
Answer false, false, "object" — an array-like object is not an array.

Task 3

const arr = [1, 2, 3];
arr.constructor = Object;
 
console.log(Array.isArray(arr));
console.log(arr instanceof Array);
console.log(arr.constructor === Array);
Answer true, true, false — changing constructor doesn't affect Array.isArray() and instanceof.

Modern capabilities

1. Using with TypeScript

// Typed checking
function processData<T>(data: T | T[]): T[] {
  if (Array.isArray(data)) {
    return data; // TypeScript knows this is T[]
  }
  return [data]; // Wrap in array
}
 
// Usage
const numbers = processData(5); // number[]
const strings = processData(["a", "b"]); // string[]

2. Checking with destructuring

function handleInput(input) {
  // Check and destructure immediately
  if (Array.isArray(input)) {
    const [first, second, ...rest] = input;
    console.log('First:', first);
    console.log('Second:', second);
    console.log('Rest:', rest);
  } else {
    console.log('Not an array:', input);
  }
}
 
handleInput([1, 2, 3, 4, 5]);
handleInput("string");

3. Checking with async/await

class AsyncArrayProcessor {
  async processArrays(data) {
    if (!Array.isArray(data)) {
      throw new Error('Expected an array');
    }
    
    const results = await Promise.all(
      data.map(async (item) => {
        // Simulate async processing
        await new Promise(resolve => setTimeout(resolve, 100));
        return item * 2;
      })
    );
    
    return results;
  }
}
 
const processor = new AsyncArrayProcessor();
 
// Usage
processor.processArrays([1, 2, 3, 4, 5])
  .then(results => console.log('Results:', results))
  .catch(error => console.error('Error:', error.message));

Best practices

✅ Recommendations

// 1. Use Array.isArray() as the main method
function isArray(value) {
  return Array.isArray(value); // ✅ Best choice
}
 
// 2. Create typed checks
function isArrayOf(array, type) {
  return Array.isArray(array) && 
         array.every(item => typeof item === type);
}
 
// 3. Document expected types
/**
 * Processes an array of numbers
 * @param {number[]} numbers - Array of numbers
 * @returns {number} Sum of numbers
 */
function sum(numbers) {
  if (!Array.isArray(numbers)) {
    throw new TypeError('Expected an array of numbers');
  }
  return numbers.reduce((acc, num) => acc + num, 0);
}
 
// 4. Use polyfill for old browsers
if (!Array.isArray) {
  Array.isArray = function(value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  };
}

❌ What to avoid

// ❌ Don't use typeof for arrays
if (typeof value === 'object') {
  // Bad! Objects, null are also object
}
 
// ❌ Don't rely only on constructor
if (value.constructor === Array) {
  // Unreliable! Can be changed
}
 
// ❌ Don't check only for method presence
if (value && value.push && value.pop) {
  // Can give false positives
}
 
// ❌ Don't use instanceof in iframe
if (value instanceof Array) {
  // May not work between frames
}

Common mistakes

1. Confusion with array-like objects

// ❌ Problem
function processItems(items) {
  // Assume it's an array
  return items.map(item => item * 2); // Error for NodeList!
}
 
const elements = document.querySelectorAll('div'); // NodeList
processItems(elements); // TypeError: items.map is not a function
 
// ✅ Solution
function processItems(items) {
  // Check and convert
  const array = Array.isArray(items) ? items : Array.from(items);
  return array.map(item => item * 2);
}

2. Incorrect handling of null/undefined

// ❌ Problem
function getLength(value) {
  if (value instanceof Array) {
    return value.length; // Error if value = null
  }
  return 0;
}
 
console.log(getLength(null)); // TypeError!
 
// ✅ Solution
function getLength(value) {
  if (Array.isArray(value)) {
    return value.length; // Safe
  }
  return 0;
}

3. Problems with nested checks

// ❌ Problem
function processNestedData(data) {
  if (Array.isArray(data)) {
    return data.map(item => {
      // Forgot to check that item can also be an array
      return item.toUpperCase(); // Error if item = []
    });
  }
}
 
// ✅ Solution
function processNestedData(data) {
  if (Array.isArray(data)) {
    return data.map(item => {
      if (Array.isArray(item)) {
        return processNestedData(item); // Recursion
      }
      return typeof item === 'string' ? item.toUpperCase() : item;
    });
  }
  return data;
}

Summary

Array checking is an important operation in JavaScript with several approaches:

Main methods:

  • Array.isArray() — most reliable and fastest
  • instanceof Array — works in most cases
  • toString.call() — universal for all types
  • constructor — unreliable, avoid

Method choice depends on:

  • 🚀 PerformanceArray.isArray() is fastest
  • 🔒 ReliabilityArray.isArray() is most reliable
  • 🌐 Browser support — all methods except Array.isArray() in IE8-
  • 🎯 Context — iframe can create problems

Recommendations:

// For most cases
Array.isArray(value);
 
// For old browsers with polyfill
if (!Array.isArray) {
  Array.isArray = function(value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  };
}
 
// For typed checks
function isArrayOf(array, type) {
  return Array.isArray(array) && array.every(item => typeof item === type);
}

Understanding the differences between methods is the foundation for reliable type work in JavaScript!


Want more interview preparation articles? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪