Facebook Pixel

2618. Check if Object Instance of Class

Problem Description

This problem asks you to implement a function checkIfInstanceOf that determines whether a given value is an instance of a specified class or any of its superclasses.

The function takes two parameters:

  • obj: any value that needs to be checked
  • classFunction: a class or constructor function to check against

The function should return true if the object is an instance of the given class or inherits from it through the prototype chain. An object is considered an instance if it has access to that class's methods through prototypal inheritance.

Key requirements:

  • The function must handle edge cases where either parameter could be null or undefined
  • There are no restrictions on data types - any JavaScript value can be passed as either parameter
  • The check should work for built-in classes (like Date, Array) as well as custom classes
  • The function needs to traverse the entire prototype chain to check for inheritance relationships

For example:

  • checkIfInstanceOf(new Date(), Date) should return true because the object is a direct instance of Date
  • checkIfInstanceOf([], Object) should return true because arrays inherit from Object
  • checkIfInstanceOf(5, Number) should return false because primitive numbers don't have Number.prototype in their prototype chain
  • If classFunction is null or undefined, the function should return false
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is understanding how JavaScript's prototype chain works. Every object in JavaScript (except for primitive values) has an internal link to another object called its prototype. When we create an instance of a class using new, the created object's prototype points to the class's prototype property.

To check if an object is an instance of a class, we need to walk up the prototype chain and see if we ever encounter the class's prototype. Think of it like climbing a family tree - if we start from a person and keep going to their parent, grandparent, and so on, we can determine if they're descended from a particular ancestor.

The approach becomes clear when we consider:

  1. Direct instances have their immediate prototype equal to classFunction.prototype
  2. Inherited instances have classFunction.prototype somewhere higher up in their prototype chain
  3. We need to handle edge cases where the class itself might be null or undefined

The solution uses Object.getPrototypeOf() to traverse the chain step by step. Starting from the object, we check if its prototype matches the class's prototype. If not, we move up to the next level (the prototype's prototype) and check again. We continue this process until either:

  • We find a match (return true)
  • We reach the end of the chain (null or undefined), meaning no inheritance relationship exists (return false)

This approach naturally handles both direct instantiation and inheritance through subclasses, as both cases result in the parent class's prototype appearing somewhere in the chain.

Solution Approach

The implementation follows a straightforward prototype chain traversal algorithm:

  1. Initial Validation: First, we check if classFunction is null or undefined. If it is, we immediately return false since there's no valid class to check against.

  2. Prototype Chain Traversal: We use a while loop to walk up the prototype chain:

    while (obj !== null && obj !== undefined) {
        const proto = Object.getPrototypeOf(obj);
        if (proto === classFunction.prototype) {
            return true;
        }
        obj = proto;
    }
  3. Step-by-step Process:

    • Start with the given object obj
    • Get its prototype using Object.getPrototypeOf(obj)
    • Compare this prototype with classFunction.prototype
    • If they match, we've found the class in the inheritance chain - return true
    • If not, move up the chain by setting obj = proto and repeat
  4. Termination Conditions:

    • Success: We find a prototype that matches classFunction.prototype
    • Failure: We reach the end of the chain (when obj becomes null or undefined)
  5. Edge Case Handling:

    • Primitive values (like numbers, strings) when boxed will have prototypes that can be traversed
    • null and undefined values for obj will cause the loop to skip entirely and return false
    • Invalid classFunction values are caught at the beginning

The algorithm has a time complexity of O(n) where n is the depth of the prototype chain, and space complexity of O(1) as we only use a constant amount of extra space for the proto variable.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to understand how the solution works. Consider checking if a custom Dog instance is an instance of Animal:

class Animal {}
class Dog extends Animal {}
const myDog = new Dog();

// Check: checkIfInstanceOf(myDog, Animal)

Step-by-step execution:

  1. Initial Validation:

    • classFunction (Animal) is not null or undefined
    • Continue to prototype chain traversal
  2. First Iteration:

    • obj = myDog (not null/undefined, so enter loop)
    • proto = Object.getPrototypeOf(myDog) → gets Dog.prototype
    • Compare: Dog.prototype === Animal.prototype? → No
    • Update: obj = Dog.prototype
  3. Second Iteration:

    • obj = Dog.prototype (not null/undefined, continue)
    • proto = Object.getPrototypeOf(Dog.prototype) → gets Animal.prototype
    • Compare: Animal.prototype === Animal.prototype? → Yes!
    • Return true

The function correctly identifies that myDog is an instance of Animal by finding Animal.prototype in the prototype chain.

Contrast with a failing case:

checkIfInstanceOf(5, Number)
  1. Initial Validation: Number is valid ✓
  2. First Iteration:
    • obj = 5 (primitive, but not null/undefined)
    • proto = Object.getPrototypeOf(5) → gets Number.prototype (via boxing)
    • Compare: Number.prototype === Number.prototype? → Wait, this should be true!

Actually, let me reconsider this example. When we call Object.getPrototypeOf(5), JavaScript boxes the primitive to a Number object temporarily. However, the primitive 5 itself doesn't have Number.prototype in its chain the way an object created with new Number(5) would.

Let's use a clearer failing example:

checkIfInstanceOf([], Date)
  1. Initial Validation: Date is valid ✓

  2. First Iteration:

    • obj = [] (array, not null/undefined)
    • proto = Object.getPrototypeOf([]) → gets Array.prototype
    • Compare: Array.prototype === Date.prototype? → No
    • Update: obj = Array.prototype
  3. Second Iteration:

    • obj = Array.prototype (not null/undefined)
    • proto = Object.getPrototypeOf(Array.prototype) → gets Object.prototype
    • Compare: Object.prototype === Date.prototype? → No
    • Update: obj = Object.prototype
  4. Third Iteration:

    • obj = Object.prototype (not null/undefined)
    • proto = Object.getPrototypeOf(Object.prototype) → gets null
    • Compare: null === Date.prototype? → No
    • Update: obj = null
  5. Fourth Iteration Check:

    • obj = null → exit loop
    • Return false

The array is not an instance of Date because Date.prototype never appears in its prototype chain.

Solution Implementation

1def checkIfInstanceOf(obj, classFunction):
2    """
3    Checks if an object is an instance of a given class/constructor function
4    by traversing the prototype chain.
5  
6    Args:
7        obj: The object to check
8        classFunction: The constructor function/class to check against
9  
10    Returns:
11        bool: True if obj is an instance of classFunction, False otherwise
12    """
13    # Handle edge cases where classFunction is None
14    if classFunction is None:
15        return False
16  
17    # In Python, we check instance relationship using isinstance() or type checking
18    # For primitive types, we need special handling
19    if obj is None:
20        return False
21  
22    # Direct instance check using isinstance
23    try:
24        # Handle primitive types that need special treatment
25        if classFunction == str and isinstance(obj, str):
26            return True
27        elif classFunction == int and isinstance(obj, int) and not isinstance(obj, bool):
28            return True
29        elif classFunction == float and isinstance(obj, (int, float)) and not isinstance(obj, bool):
30            return True
31        elif classFunction == bool and isinstance(obj, bool):
32            return True
33        elif classFunction == list and isinstance(obj, list):
34            return True
35        elif classFunction == dict and isinstance(obj, dict):
36            return True
37        elif classFunction == tuple and isinstance(obj, tuple):
38            return True
39        elif classFunction == set and isinstance(obj, set):
40            return True
41        # For custom classes and built-in types, use isinstance
42        elif isinstance(obj, classFunction):
43            return True
44        # Check if obj's type is a subclass of classFunction
45        elif isinstance(type(obj), type) and issubclass(type(obj), classFunction):
46            return True
47    except (TypeError, AttributeError):
48        # If classFunction is not a valid type/class, return False
49        return False
50  
51    # No match found
52    return False
53
54
55# Example usage:
56# from datetime import datetime
57# checkIfInstanceOf(datetime.now(), datetime)  # True
58# checkIfInstanceOf("hello", str)  # True
59# checkIfInstanceOf(5, int)  # True
60# checkIfInstanceOf(None, list)  # False
61# checkIfInstanceOf([], object)  # True
62
1/**
2 * Checks if an object is an instance of a given class/constructor function
3 * by traversing the inheritance chain.
4 * 
5 * @param obj - The object to check
6 * @param classFunction - The class to check against
7 * @return true if obj is an instance of classFunction, false otherwise
8 */
9public static boolean checkIfInstanceOf(Object obj, Class<?> classFunction) {
10    // Handle edge cases where classFunction is null
11    if (classFunction == null) {
12        return false;
13    }
14  
15    // Handle null object case
16    if (obj == null) {
17        return false;
18    }
19  
20    // Get the class of the object
21    Class<?> currentClass = obj.getClass();
22  
23    // Traverse the inheritance chain of the object
24    while (currentClass != null) {
25        // Check if the current class matches the target class
26        if (currentClass.equals(classFunction)) {
27            return true;
28        }
29      
30        // Check interfaces implemented by the current class
31        Class<?>[] interfaces = currentClass.getInterfaces();
32        for (Class<?> iface : interfaces) {
33            if (iface.equals(classFunction)) {
34                return true;
35            }
36            // Recursively check parent interfaces
37            if (checkInterfaceHierarchy(iface, classFunction)) {
38                return true;
39            }
40        }
41      
42        // Move up the inheritance chain to the superclass
43        currentClass = currentClass.getSuperclass();
44    }
45  
46    // No match found in the inheritance chain
47    return false;
48}
49
50/**
51 * Helper method to check interface hierarchy recursively
52 * 
53 * @param iface - The interface to check
54 * @param targetClass - The target class to match against
55 * @return true if targetClass is found in the interface hierarchy
56 */
57private static boolean checkInterfaceHierarchy(Class<?> iface, Class<?> targetClass) {
58    // Get parent interfaces of the current interface
59    Class<?>[] parentInterfaces = iface.getInterfaces();
60  
61    // Check each parent interface
62    for (Class<?> parentInterface : parentInterfaces) {
63        if (parentInterface.equals(targetClass)) {
64            return true;
65        }
66        // Recursively check the parent interface's hierarchy
67        if (checkInterfaceHierarchy(parentInterface, targetClass)) {
68            return true;
69        }
70    }
71  
72    return false;
73}
74
75/**
76 * Example usage:
77 * checkIfInstanceOf(new Date(), Date.class); // true
78 * checkIfInstanceOf("hello", String.class); // true
79 * checkIfInstanceOf(5, Integer.class); // true (autoboxed)
80 * checkIfInstanceOf(null, ArrayList.class); // false
81 * checkIfInstanceOf(new ArrayList<>(), List.class); // true
82 */
83
1#include <typeinfo>
2#include <type_traits>
3
4/**
5 * Checks if an object is an instance of a given class/type
6 * by using C++ RTTI (Run-Time Type Information).
7 * 
8 * Note: C++ doesn't have prototype chains like JavaScript.
9 * This implementation uses dynamic_cast for polymorphic types
10 * and type traits for primitive types.
11 * 
12 * @tparam T - The type of the object to check
13 * @tparam ClassType - The class/type to check against
14 * @param obj - Pointer to the object to check
15 * @returns true if obj is an instance of ClassType, false otherwise
16 */
17template<typename ClassType, typename T>
18bool checkIfInstanceOf(T* obj) {
19    // Handle null pointer case
20    if (obj == nullptr) {
21        return false;
22    }
23  
24    // For polymorphic types, use dynamic_cast
25    // dynamic_cast returns nullptr if the cast fails
26    if constexpr (std::is_polymorphic_v<T> && std::is_polymorphic_v<ClassType>) {
27        return dynamic_cast<ClassType*>(obj) != nullptr;
28    }
29    // For non-polymorphic types, use compile-time type checking
30    else {
31        return std::is_base_of_v<ClassType, T> || std::is_same_v<ClassType, T>;
32    }
33}
34
35/**
36 * Overload for checking primitive types and values
37 * 
38 * @tparam T - The type to check
39 * @tparam ClassType - The type to check against
40 * @param obj - The object value (by reference)
41 * @returns true if obj is of type ClassType, false otherwise
42 */
43template<typename ClassType, typename T>
44bool checkIfInstanceOf(const T& obj) {
45    // Use type traits to check if types match
46    return std::is_same_v<std::decay_t<T>, ClassType>;
47}
48
49/**
50 * Example usage:
51 * 
52 * class Base { virtual ~Base() {} };
53 * class Derived : public Base {};
54 * 
55 * Derived d;
56 * Base* b = &d;
57 * 
58 * checkIfInstanceOf<Base>(&d);     // true
59 * checkIfInstanceOf<Derived>(b);   // true
60 * checkIfInstanceOf<int>(5);       // true
61 * checkIfInstanceOf<double>(5);    // false
62 * checkIfInstanceOf<Base>(nullptr); // false
63 */
64
1/**
2 * Checks if an object is an instance of a given class/constructor function
3 * by traversing the prototype chain.
4 * 
5 * @param obj - The object to check
6 * @param classFunction - The constructor function/class to check against
7 * @returns true if obj is an instance of classFunction, false otherwise
8 */
9function checkIfInstanceOf(obj: any, classFunction: any): boolean {
10    // Handle edge cases where classFunction is null or undefined
11    if (classFunction === null || classFunction === undefined) {
12        return false;
13    }
14  
15    // Traverse the prototype chain of the object
16    while (obj !== null && obj !== undefined) {
17        // Get the prototype of the current object
18        const proto: any = Object.getPrototypeOf(obj);
19      
20        // Check if the prototype matches the classFunction's prototype
21        if (proto === classFunction.prototype) {
22            return true;
23        }
24      
25        // Move up the prototype chain
26        obj = proto;
27    }
28  
29    // No match found in the prototype chain
30    return false;
31}
32
33/**
34 * Example usage:
35 * checkIfInstanceOf(new Date(), Date); // true
36 * checkIfInstanceOf("hello", String); // true
37 * checkIfInstanceOf(5, Number); // true
38 * checkIfInstanceOf(null, Array); // false
39 * checkIfInstanceOf([], Object); // true
40 */
41

Time and Space Complexity

Time Complexity: O(n) where n is the depth of the prototype chain from the object to its root prototype (null).

The function traverses the prototype chain using a while loop, starting from the given object and moving up through each prototype level using Object.getPrototypeOf(). In the worst case, it needs to traverse the entire prototype chain until reaching null. Each iteration performs constant time operations: getting the prototype (O(1)) and comparing it with classFunction.prototype (O(1)).

Space Complexity: O(1) - constant space complexity.

The function only uses a fixed amount of extra space regardless of the input size. It reuses the same variable obj to traverse the prototype chain and uses a single variable proto to store the current prototype. No additional data structures that grow with input size are created, and the function doesn't use recursion (which would create a call stack proportional to the prototype chain depth).

Common Pitfalls

1. Confusing JavaScript Prototype Chain with Python's Type System

The provided Python solution doesn't accurately implement the JavaScript prototype chain traversal logic. Python uses a different inheritance model based on classes and metaclasses, not prototypes. The solution attempts to map JavaScript behavior to Python, but this creates several issues:

Problem:

# This Python code doesn't actually traverse a prototype chain
if isinstance(obj, classFunction):
    return True

Better Approach:

def checkIfInstanceOf(obj, classFunction):
    if classFunction is None:
        return False
  
    if obj is None:
        return False
  
    # Use Python's MRO (Method Resolution Order) to simulate prototype chain
    obj_type = type(obj)
  
    # Check direct instance
    if obj_type == classFunction:
        return True
  
    # Traverse the inheritance hierarchy using __mro__
    for base_class in obj_type.__mro__:
        if base_class == classFunction:
            return True
  
    return False

2. Incorrect Handling of Primitive Types

The solution has redundant and incorrect handling of primitive types. In JavaScript, primitives like 5 don't have Number.prototype in their chain, but the Python code treats them as instances:

Problem:

elif classFunction == int and isinstance(obj, int):
    return True  # This would return True for checkIfInstanceOf(5, int)

Correct Behavior (JavaScript-aligned):

def checkIfInstanceOf(obj, classFunction):
    # Primitives in JavaScript don't have prototypes in their chain
    # Only their boxed versions do
    if classFunction is None:
        return False
  
    # Check if obj is a primitive (not an object)
    primitive_types = (int, float, str, bool, type(None))
    if type(obj) in primitive_types:
        # Primitives return false unless explicitly boxed
        return False
  
    # For objects, check the inheritance chain
    return isinstance(obj, classFunction) if classFunction else False

3. Missing Prototype Chain Simulation

The original JavaScript solution traverses the prototype chain using Object.getPrototypeOf(). The Python version doesn't simulate this behavior:

JavaScript-style Implementation in Python:

def checkIfInstanceOf(obj, classFunction):
    if classFunction is None:
        return False
  
    if obj is None:
        return False
  
    # Simulate prototype chain traversal
    current_class = obj.__class__
  
    while current_class is not None:
        if current_class == classFunction:
            return True
      
        # Move up the inheritance chain
        bases = current_class.__bases__
        if bases:
            current_class = bases[0]  # Follow first base class
        else:
            break
  
    # Check if classFunction is 'object' (everything inherits from object in Python)
    if classFunction == object and obj is not None:
        return True
  
    return False

4. Exception Handling Too Broad

The try-except block catches TypeError and AttributeError but doesn't distinguish between different error causes:

Problem:

except (TypeError, AttributeError):
    return False  # Silently fails for all errors

Better Approach:

def checkIfInstanceOf(obj, classFunction):
    if classFunction is None:
        return False
  
    if obj is None:
        return False
  
    # Check if classFunction is actually a class/type
    if not isinstance(classFunction, type):
        return False
  
    try:
        return isinstance(obj, classFunction)
    except TypeError as e:
        # Log or handle specific error cases
        if "isinstance() arg 2" in str(e):
            return False  # classFunction is not a valid type
        raise  # Re-raise unexpected errors

5. Inconsistent Boolean Handling

The code treats bool specially but bool is a subclass of int in Python, leading to potential confusion:

Problem:

elif classFunction == int and isinstance(obj, int) and not isinstance(obj, bool):
    return True  # Excludes booleans from int check

Clearer Approach:

def checkIfInstanceOf(obj, classFunction):
    if classFunction is None or obj is None:
        return False
  
    # Handle bool/int relationship explicitly
    if classFunction == int and isinstance(obj, bool):
        # In Python, bool is a subclass of int
        # Decide based on requirements
        return True  # or False, depending on specification
  
    return isinstance(obj, classFunction)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Depth first search is equivalent to which of the tree traversal order?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More