title: Check if Object Instance of Class
slug: check-if-object-instance-of-class
difficulty: medium
leetcode_id: 2618
leetcode_url: https://leetcode.com/problems/check-if-object-instance-of-class/
categories:
  - recursion
patterns:
  - dfs

function_signature: "function checkIfInstanceOf(obj, classFunction)"

test_cases:
  visible:
    - input: { obj: "new Date()", classFunction: "Date" }
      expected: true
    - input: { obj: "5", classFunction: "Number" }
      expected: true
    - input: { obj: "Date", classFunction: "Date" }
      expected: false
  hidden:
    - input: { obj: "null", classFunction: "Object" }
      expected: false
    - input: { obj: "undefined", classFunction: "Object" }
      expected: false
    - input: { obj: "'hello'", classFunction: "String" }
      expected: true
    - input: { obj: "[]", classFunction: "Array" }
      expected: true
    - input: { obj: "[]", classFunction: "Object" }
      expected: true
    - input: { obj: "{}", classFunction: "Object" }
      expected: true

description: |
  Write a function that checks if a given value is an instance of a given class or superclass. For this problem, an object is considered an instance of a given class if that object has access to that class's methods.

  There are no constraints on the data types that can be passed to the function. For example, the value or the class could be `undefined`.

constraints: |
  - `value` is any valid JavaScript value
  - `classFunction` is any valid JavaScript value

examples:
  - input: "func = () => checkIfInstanceOf(new Date(), Date)"
    output: "true"
    explanation: "The object returned by the Date constructor is, by definition, an instance of Date."
  - input: "func = () => { class Animal {}; class Dog extends Animal {}; return checkIfInstanceOf(new Dog(), Animal); }"
    output: "true"
    explanation: "Dog is a subclass of Animal. Therefore, a Dog object is an instance of both Dog and Animal."
  - input: "func = () => checkIfInstanceOf(Date, Date)"
    output: "false"
    explanation: "A date constructor cannot logically be an instance of itself."
  - input: "func = () => checkIfInstanceOf(5, Number)"
    output: "true"
    explanation: "5 is a Number. Note that the 'instanceof' keyword would return false. However, it is still considered an instance of Number because it accesses the Number methods. For example 'toFixed()'."

explanation:
  intuition: |
    Think of JavaScript's prototype chain as a family tree. Every object has a "parent" (its prototype), and that parent may have its own parent, forming a chain all the way up to `null`.

    When we check if an object is an "instance" of a class, we're really asking: **"Is this class's prototype anywhere in the object's ancestry?"**

    The twist in this problem is that JavaScript's native `instanceof` operator doesn't work with primitives like `5` or `"hello"`. These primitives aren't technically objects, yet they *can* access methods from their wrapper types (`Number`, `String`). For example, `(5).toFixed(2)` works because JavaScript temporarily "boxes" the primitive into its object wrapper.

    So our solution needs to handle both objects and primitives. The key insight is that `Object(value)` converts any value to its object form — primitives get wrapped, and objects stay unchanged. Once we have an object, we can walk up its prototype chain looking for our target class.

  approach: |
    We solve this by **walking the prototype chain** after handling edge cases:

    **Step 1: Handle null and undefined values**

    - If `value` is `null` or `undefined`, return `false` immediately
    - These values have no prototype chain to traverse

    &nbsp;

    **Step 2: Validate the class function**

    - If `classFunction` is not a function, return `false`
    - Only functions have a `prototype` property we can compare against

    &nbsp;

    **Step 3: Convert primitives to objects**

    - Use `Object(value)` to wrap primitives in their object form
    - This allows `5` to become a `Number` object, `"hi"` to become a `String` object
    - Objects pass through unchanged

    &nbsp;

    **Step 4: Walk the prototype chain**

    - Start with `Object.getPrototypeOf(obj)` to get the object's prototype
    - Compare each prototype with `classFunction.prototype`
    - If they match, return `true`
    - If we reach `null` (end of chain), return `false`

    &nbsp;

    This approach correctly handles inheritance: when checking `new Dog()` against `Animal`, we find `Animal.prototype` further up the chain from `Dog.prototype`.

  common_pitfalls:
    - title: Using Native instanceof
      description: |
        The native `instanceof` operator fails for primitives:

        ```javascript
        5 instanceof Number  // false
        "hi" instanceof String  // false
        ```

        But per the problem definition, `5` *is* an instance of `Number` because it can access `Number` methods. We must handle primitives by converting them to their object wrappers first.
      wrong_approach: "return value instanceof classFunction"
      correct_approach: "Convert primitives with Object(value), then traverse prototype chain"

    - title: Forgetting Edge Cases
      description: |
        Both `value` and `classFunction` can be any JavaScript value, including `null`, `undefined`, or non-functions.

        - `checkIfInstanceOf(null, Object)` should return `false` (null has no prototype)
        - `checkIfInstanceOf({}, undefined)` should return `false` (undefined isn't a class)
        - `checkIfInstanceOf({}, {})` should return `false` (objects aren't constructor functions)

        Always validate inputs before traversing the prototype chain.
      wrong_approach: "Assume value is an object and classFunction is a function"
      correct_approach: "Check for null/undefined and verify classFunction is a function"

    - title: Infinite Loop on Prototype Chain
      description: |
        The prototype chain always terminates at `null`. Forgetting to check for this termination condition leads to errors or infinite loops.

        ```javascript
        Object.getPrototypeOf(Object.prototype)  // null
        ```

        Your while loop must stop when the prototype becomes `null`.
      wrong_approach: "while (proto) without null check"
      correct_approach: "while (proto !== null) with explicit termination"

  key_takeaways:
    - "**Prototype chain traversal**: JavaScript inheritance works through prototype chains — understanding this is fundamental to JavaScript"
    - "**Primitives vs objects**: Primitives like `5` and `\"hello\"` can access methods because JavaScript temporarily boxes them, but they fail `instanceof` checks"
    - "**Object() wrapper**: `Object(value)` is a powerful technique to normalise primitives and objects for uniform handling"
    - "**Defensive programming**: Always validate inputs in JavaScript — any value can be passed to any function"

  time_complexity: "O(d) where d is the depth of the prototype chain. For most objects, this is a small constant (typically 2-4 levels)."
  space_complexity: "O(1). We only use a single pointer variable to traverse the chain."

solutions:
  - approach_name: Prototype Chain Traversal
    is_optimal: true
    language: javascript
    code: |
      function checkIfInstanceOf(obj, classFunction) {
          // Handle null/undefined - they have no prototype chain
          if (obj === null || obj === undefined) {
              return false;
          }

          // classFunction must be a function to have a prototype
          if (typeof classFunction !== 'function') {
              return false;
          }

          // Convert primitives to their object wrappers
          // This allows 5 to become Number, "hi" to become String, etc.
          obj = Object(obj);

          // Walk up the prototype chain
          let proto = Object.getPrototypeOf(obj);

          while (proto !== null) {
              // Found a match in the prototype chain
              if (proto === classFunction.prototype) {
                  return true;
              }
              // Move to the next prototype in the chain
              proto = Object.getPrototypeOf(proto);
          }

          // Reached end of chain without finding a match
          return false;
      }
    explanation: |
      **Time Complexity:** O(d) — We traverse at most d prototypes where d is the chain depth.

      **Space Complexity:** O(1) — Only one pointer variable used.

      We first handle edge cases (null/undefined values, non-function classes), then convert primitives to objects. Finally, we walk up the prototype chain comparing each prototype against our target class's prototype.

  - approach_name: Recursive Prototype Check
    is_optimal: false
    language: javascript
    code: |
      function checkIfInstanceOf(obj, classFunction) {
          // Handle null/undefined
          if (obj === null || obj === undefined) {
              return false;
          }

          // classFunction must be a function
          if (typeof classFunction !== 'function') {
              return false;
          }

          // Recursive helper to check prototype chain
          function checkProto(proto) {
              // Base case: reached end of chain
              if (proto === null) {
                  return false;
              }
              // Found match
              if (proto === classFunction.prototype) {
                  return true;
              }
              // Recurse up the chain
              return checkProto(Object.getPrototypeOf(proto));
          }

          // Start from the object's prototype
          return checkProto(Object.getPrototypeOf(Object(obj)));
      }
    explanation: |
      **Time Complexity:** O(d) — Same traversal depth as iterative approach.

      **Space Complexity:** O(d) — Recursive call stack grows with chain depth.

      This recursive approach is conceptually cleaner but uses extra stack space. The iterative version is preferred for its O(1) space complexity.
