questions C
This commit is contained in:
206
backend/data/questions/check-if-object-instance-of-class.yaml
Normal file
206
backend/data/questions/check-if-object-instance-of-class.yaml
Normal file
@@ -0,0 +1,206 @@
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
**Step 2: Validate the class function**
|
||||
|
||||
- If `classFunction` is not a function, return `false`
|
||||
- Only functions have a `prototype` property we can compare against
|
||||
|
||||
|
||||
|
||||
**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
|
||||
|
||||
|
||||
|
||||
**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`
|
||||
|
||||
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user