instanceof 你真的用对了吗?

130 阅读3分钟

instanceof 你真的用对了吗?

在 JavaScript 中,instanceof 是一个常用的运算符,用于检测对象是否是某个构造函数的实例。然而,默认情况下它并不能直接用于判断基本数据类型(如 stringnumberboolean 等)。

一、instanceof 的基本用法

instanceof 的语法如下:

object instanceof constructor

它用于检查 object 是否是 constructor 的实例。具体来说,它会检查 constructor.prototype 是否存在于 object 的原型链上。

示例:

function Person() {}
const p = new Person();

console.log(p instanceof Person); // true
console.log(p instanceof Object); // true

二、instanceof能不能用于判断基本数据类型?

首先我们需要了解instanceof 是怎么工作的?

obj instanceof Constructor

当执行上述代码时JavaScript 会做以下事情:

  • 查看 Constructor[Symbol.hasInstance] 是否存在。
  • 如果存在,就调用这个方法,传入 obj
  • 如果没有,就按照原型链来判断(默认行为)。

所以若构造函数或类中没有重写Symbol.hasInstance,由于JavaScript 中的基本数据类型(如 numberstringboolean)不是对象,它们没有原型链。因此,使用 instanceof 判断基本类型会返回 false。例如:

console.log(123 instanceof Number); // false
console.log("hello" instanceof String); // false
console.log(true instanceof Boolean); // false

但在了解了instanceof 的工作机制后我们可以通过Symbol.hasInstance来自定义instanceof 操作符在某个类上的行为。

  • Symbol.hasInstance 是一个内建符号,用于确定一个对象是否是某个构造函数的实例。
  • 默认情况下,instanceof 会检查原型链(即 constructor.prototype 是否存在于对象的原型链上)。
  • 通过自定义 Symbol.hasInstance,可以改变 instanceof 的行为。

例如:

class Number {
  static [Symbol.hasInstance](instance) {
    return typeof instance === 'number';
  }
}

console.log(1 instanceof Number) // true

上述代码的逻辑

  1. 定义一个类 Number

    这里定义了一个类,叫做 Number。目前这个类没有任何内容,只是个空类。

    class Number {}
    
  2. 添加一个静态方法 [Symbol.hasInstance]

    在这个方法中可以自定义instanceof的判断逻辑

     static [Symbol.hasInstance](instance) {
         return typeof instance === 'number'
     }
    

    这是关键部分:

    • static 表示这是一个静态方法(属于类本身,不属于实例)。

      必须是静态方法,因为 JavaScript 的 instanceof 运算符只会在构造函数上查找 Symbol.hasInstance,而不是在实例或原型上。

    • [Symbol.hasInstance] 是一个动态属性名,表示这个方法的名字是 Symbol.hasInstance

    • (instance) 是参数,表示被判断的对象。

    所以这行代码的意思是:给 Number 类添加一个静态方法,这个方法会在使用 instanceof 时被调用。

其他重写Symbol.hasInstance方式

  1. 直接给类赋值

    class Number {}
    
    Number[Symbol.hasInstance] = function(instance) {
      return typeof instance === 'number';
    };
    
  2. 普通函数来构造类

    function Number() {}
    
    Number[Symbol.hasInstance] = function(instance) {
      return typeof instance === 'number';
    };
    

三、手动实现一个 instanceof

要手动实现一个类似于 instanceof 的功能,我们需要检查构造函数的 prototype 是否存在于对象的原型链中。

实现思路:

  1. 获取对象的原型;
  2. 获取构造函数的原型对象;
  3. 从对象的原型开始,沿着原型链向上查找;
  4. 如果找到构造函数的原型对象,返回 true
  5. 如果查找到原型链顶端(null)仍未找到,返回 false

手写实现:

function myInstanceOf(obj, constructor) {
    // 获取构造函数的 prototype
    const prototype = constructor.prototype;

    // 获取对象的原型
    let currentProto = Object.getPrototypeOf(obj);

    // 沿着原型链查找
    while (currentProto !== null) {
        if (currentProto === prototype) {
            return true;
        }
        currentProto = Object.getPrototypeOf(currentProto);
    }

    return false;
}

测试:

function Person() {}
const p = new Person();

console.log(myInstanceOf(p, Person)); // true
console.log(myInstanceOf(p, Object)); // true
console.log(myInstanceOf(p, Array));  // false

console.log(myInstanceOf(123, Number)); // false
console.log(myInstanceOf(new Number(123), Number)); // true

可以看到,基本类型 123 不是 Number 的实例,而 new Number(123) 是。