How Symbol.hasInstance Overrides instanceof in JS

In JavaScript, the instanceof operator traditionally verifies if an object inherits from a constructor’s prototype chain. With the introduction of ES6, JavaScript exposed the Symbol.hasInstance well-known symbol, allowing developers to directly customize and override the behavior of the instanceof operator. This article explains how Symbol.hasInstance works internally, how to implement it in your classes and objects, and practical scenarios for customizing instance checks.

How the instanceof Operator Works Internally

Historically, evaluating obj instanceof Constructor caused the JavaScript engine to traverse obj’s prototype chain to check if Constructor.prototype was present.

In ES6 and later, the syntax left instanceof Right is translated into a method call on the right-hand operand:

Right[Symbol.hasInstance](left)

If the method exists, its return value determines the outcome of the instanceof expression. If it is omitted, the engine falls back to default prototype chain traversal using Function.prototype[Symbol.hasInstance].

Overriding instanceof in ES6 Classes

To customize instanceof validation inside an ES6 class, define a static method using the computed property name [Symbol.hasInstance]. This method receives the value on the left side of the instanceof operator as its sole argument and must return a boolean value.

class EvenNumber {
  static [Symbol.hasInstance](instance) {
    return typeof instance === 'number' && instance % 2 === 0;
  }
}

console.log(4 instanceof EvenNumber); // true
console.log(7 instanceof EvenNumber); // false
console.log('4' instanceof EvenNumber); // false

In this example, EvenNumber is not instantiated using the new keyword. Instead, the instanceof operator checks if the value fulfills a mathematical condition rather than checking prototype inheritance.

Using Symbol.hasInstance on Plain Objects

Because Symbol.hasInstance is a symbol property, it can also be attached to standard objects using Object.defineProperty. This allows plain objects to serve as the right-hand operand for instanceof checks without defining a class or constructor function.

const ArrayLike = {
  [Symbol.hasInstance](instance) {
    return (
      instance != null &&
      typeof instance[Symbol.iterator] === 'function' &&
      typeof instance.length === 'number'
    );
  }
};

console.log([1, 2, 3] instanceof ArrayLike); // true
console.log('hello' instanceof ArrayLike);    // true
console.log({ a: 1 } instanceof ArrayLike);   // false

Common Use Cases

1. Structural Typing (Duck Typing)

Instead of enforcing strict class hierarchy, you can check whether an object implements specific properties or methods.

class Serializable {
  static [Symbol.hasInstance](obj) {
    return obj != null && typeof obj.toJSON === 'function';
  }
}

const customData = {
  id: 1,
  toJSON() { return JSON.stringify(this); }
};

console.log(customData instanceof Serializable); // true

2. Cross-Realm and Multi-Window Validation

When dealing with objects created in different execution contexts (such as <iframe> elements or Node.js vm contexts), built-in instanceof checks fail because prototypes differ across realms. Customizing Symbol.hasInstance allows you to validate types using universal tags (like Symbol.toStringTag) rather than fragile prototype references.

3. Custom Primitive Type Guards

Standard instanceof checks evaluate to false for primitive types (e.g., 42 instanceof Number is false). By implementing Symbol.hasInstance, you can extend custom type validation to include primitive values.

Key Considerations