說說instanceof和typeof的實現原理並自己模擬實現一個instanceof

王铁柱6發表於2024-11-23

Let's break down the typeof and instanceof operators in JavaScript and then create a custom instanceof implementation.

typeof Operator

  • Implementation Principle: The typeof operator determines the primitive type of a value. It's a unary operator (takes one operand) and returns a string indicating the type. Internally, it checks the internal [[Type]] property of the value. This property isn't directly accessible to JavaScript code, but typeof provides a way to query it.

  • Limitations: typeof is primarily useful for primitive types. For objects (including arrays and functions), it usually returns "object," except for functions where it returns "function." This makes it less helpful for distinguishing between different object types. typeof null notoriously returns "object," which is a historical bug.

instanceof Operator

  • Implementation Principle: The instanceof operator checks if an object is an instance of a particular constructor (or a constructor in its prototype chain). It works by traversing the prototype chain of the object. If it encounters a prototype that matches the constructor's prototype, it returns true. Otherwise, if it reaches the end of the prototype chain without a match, it returns false.

  • How it works with prototypes: Every object in JavaScript has a prototype (accessible via __proto__ or Object.getPrototypeOf()). When you create an object using a constructor function (e.g., new MyClass()), the object's prototype is set to the constructor's prototype property. instanceof leverages this relationship to determine inheritance.

Custom instanceof Implementation

function myInstanceof(obj, constructor) {
  if (obj === null || typeof obj !== 'object') {
    return false; // null and non-objects are not instances of anything
  }

  let proto = Object.getPrototypeOf(obj);

  while (proto) {
    if (proto === constructor.prototype) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }

  return false; // Reached end of prototype chain without a match
}


// Example usage:
function MyClass() {}
const myObj = new MyClass();

console.log(myInstanceof(myObj, MyClass)); // true
console.log(myInstanceof({}, Object));       // true
console.log(myInstanceof([], Array));        // true
console.log(myInstanceof([], Object));        // true (Arrays inherit from Object)
console.log(myInstanceof(null, Object));      // false (Handles the null case)
console.log(myInstanceof(5, Number));        // false (Handles primitives)
console.log(myInstanceof(myObj, Array));     // false

// Demonstrating with a more complex prototype chain
function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype);
const childObj = new Child();

console.log(myInstanceof(childObj, Child));  // true
console.log(myInstanceof(childObj, Parent)); // true (Inheritance)

Key improvements in the custom implementation:

  • Handles null correctly: Explicitly checks for null and returns false.
  • Handles non-objects: Checks if the left-hand operand is an object. If not, it returns false. This prevents errors when used with primitives.
  • Uses Object.getPrototypeOf(): This is the preferred way to access the prototype. While __proto__ works, it's deprecated for general use.

This custom implementation closely mirrors the behavior of the built-in instanceof operator, providing a clearer understanding of its underlying mechanism. Remember that built-in operators are generally optimized, so in performance-critical scenarios, using the native instanceof is recommended. However, for understanding and learning, building a custom version is extremely valuable.

相關文章